﻿#define _CRT_SECURE_NO_WARNINGS 1

//C++学习记录

//C++初阶
#include <stdio.h>
//
////命名空间 namespace
////命名空间是为了解决	用户定义变量名与库中变量名冲突问题
////						多人协作代码时,命名冲突的问题
//
////C/C++中一般有全局域	局部域	静态域
////编译器在编译过程中对变量首先在局部域进行检索,之后会在全局域进行检索,找不到就会进行报错
//
////C++中引入了一种语法	命名空间	对创建的全局变量进行隔离,隔离在一个域中,不同的域中可以拥有相同名称的变量
////命名空间是一个域,一个全局的静态域, 域中定义声明的变量均为全局变量
//
//namespace N			//命名空间可以进行嵌套操作
//{	
//	//在命名空间的域中并不会影响改变内部成员的属性,只是对其进行一个包装隔离,改变编译器的检索策略
//	//命名空间中可以放入	变量 / 类型 / 声明 / 函数
//	
//	int num = 0;
//	namespace N1		//实际域名为	N::N1
//	{					//被嵌套的域的实际域名要加上嵌套的域的域名
//		int a = 10;
//		struct Node
//		{
//			int x;
//		};
//	}
//	void Test(void);
//	extern int ext;
//	struct ABC;
//}
//namespace N			//当编译器遇到相同的域名时,会进行合并操作
//{
//	int num2 = 0;
//}
//namespace N1			//该域与N中的N1域不是同名域,不会合并,但若在会进行合并的场景中,会报错
//{
//	int a = 1;
//}
//
////可以在全局域中为某一域中的函数/声明/类型进行创建,且创建出的成员仅属于该域
//void N::Test(void)
//{
//	printf("N::Test\n");
//}
//int N::ext;
//struct N::ABC
//{
//	int a;
//};
//
//int st = 10;
//int main()
//{
//	//编译器不会主动进入用户定义的域中去寻找变量
//	//引用自定义域中的变量操作符	域名::域中成员名	(全局域的域名为空白)
//	N::num = 1;					//命名空间的第一种使用方式,	加命名空间名称以及作用域限定符
//
//	using N::N1::a;				//命名空间的第二种使用方式, 使用 using 将命名空间中某个成员引入全局域中
//	//该句相当于一句声明变量a所在域
//	printf("N::N1::a = %d\n", a);
//
//	//using namespace N;			//将整个命名空间引入,慎重使用,该操作取消了域对内部成员的包装隔离
//	//using的引入是局部的,即从引入开始到其引入的域结束			//但其内部嵌套域并不会递归引入,其引入操作仅引入自身的内部成员
//
//	struct N::N1::Node n1 = { 0 };		//多层嵌套域引用成员
//	N::N1::Node node = { 0 };			//在引用域中结构体成员时 struct 可以不用,C++中结构体名称可以省略struct
//
//	int st = 1;
//	printf("st = %d\n", st);
//	printf("::st = %d\n", ::st);	//在局部域与全局域冲突时,引用全局域中变量 ::成员名
//							//所有全局变量在使用时已自动省略对全局域的引用
//	N::Test();
//	printf("ext = %d\n", N::ext);
//
//	return 0;
//}

//C++的输入 & 输出
//C++输入输出流 iostream	#include <iostream>
//部分老版编译器不支持 namespace C++输入输出流引用为	#include <iostream.h>
#include <iostream>
//一般在 iostream 头文件中包含了 stdio.h
//在项目协作时不建议将std全部展开,会使std的隔离失效		一般使用C++库函数时指定域名 std::函数名
using namespace std;		//C++库的实现定义在一个叫 std 的命名空间中
//int main()
//{
//	//使用 cout 标准输出对象(控制台) 和 cin 标准输入对象(键盘)时,必须包含 iostream 头文件及按照命名空间使用方法使用std
//
//	//cout 是属于iostream中的一个全局符号	输出流
//	cout << "Hello World" << endl;		//endl是C++中添加的一个单独的换行符 包含在iostream中
//		// << 流 输出/插入 运算符
//	
//	int i = 11;
//	double d = 11.1;
//	//cout 的特性自动识别变量类型	但小数点1位之后会自动省略一位精度
//	cout << i << ' ' << d << endl;	//可以单独流插入一个字符
//	//cout << i << " " << d << endl;	效果一样
//	
//	//cin 输出流	也可以自动识别变量类型	小数点1位之后会自动省略一位精度
//	cin >> i >> d;		//使用 cin 对变量进行赋值时,不用使用&
//	printf("%lf\n", d);
//	// >> 流提取 运算符
//	cout << i << ' ' << d << endl;
//	//C++兼容C语言,也可以使用printf进行输出,scanf进行输入
//	scanf("%d %lf", &i, &d);
//	cout << d << endl;
//	printf("%d %lf\n", i, d);
//
//	return 0;
//}

//缺省参数
//是声明或者定义函数时为函数的参数指定一个缺省值
//在调用该函数时,如果该位置的形参没有指定实参则采用该形参的缺省值,否则使用指定实参
//int gs = 10;
//全缺省参数	所有参数全部都赋有缺省值
//void Func(int a = 0, int b = gs)		//缺省参数不能在函数声明和定义中同时出现(无论数值是否相等)	推荐写在声明中
//{		//缺省值必须是常量或者全局变量
//	cout << a << ' ' << b << endl;
//}
//int main()
//{
//	//没有传参数时,使用指定的默认参数
//	Func();			//0 10
//	//传参时,使用指定的实参
//	Func(1);		//1 10
//	Func(1, 2);		//1 2
//	return 0;
//}
//半缺省	缺省部分参数
//void Func(int a, int b, int c = 10, int d = 20)
//{		//必须从右向左连续缺省
//	cout << a << ' ' << b << ' ';
//	cout << c << ' ' << d << endl;
//}
//int main()
//{
//  //对函数参数进行传参的必须从左向右连续传参
//	Func(1, 2);			//1 2 10 20
//	Func(1, 2, 3);		//1 2 3 20
//	Func(1, 2, 3, 4);	//1 2 3 4
//	return 0;
//}

//函数重载
//是函数的一种特殊情况
//C++中允许在 同一作用域 中声明几个功能类似的 同名函数
//但这些同名函数的形参列表(参数 个数/类型/顺序)不同		返回值不同不能构成重载	调用的时候编译器不能进行区分
//常用来处理实现功能类似,数据类型不同的问题
//C语言不支持函数重载,因为在编译时,两个重载函数函数名相同
//在 func.o 中,符号表存在歧义和冲突
//在  链接  中,也存在歧义和冲突
//因为他们都是直接使用函数名称去进行标识和查找
//而重载函数,虽然函数名相同,但C++的目标文件符号表中不是直接使用函数名来标识和查找函数
//引入了:函数名称修饰规则(该规则在不同的编译器下不一定相同)
//g++下,void f() --> <_Z1fv>
//					 _Z 前缀	1 函数名长度	f 函数名	v函数参数(取类型首字符,若为指针在其前+P,若为引用在其前+R)
//函数地址的查找
//若在当前文件中含有函数的定义,编译期间就可以填上函数地址
//若在当前文件中只有声明,在其他文件中有定义,则编译期间没有地址,在链接期间对其他.o文件的符号表进行查找
//										  //call <_Z1fv(?)>						call <_Z1fv(地址)>
//有了函数名修饰规则,只要参数不同,所在文件生成的符号表中重载函数就不存在冲突
//链接时,调用重载函数查找地址时就不会有歧义
//参数类型不同
//int Add(int left, int right)
//{
//	cout << "int Add(int left, int right)" << endl;
//	return left + right;
//}
//double Add(double left, double right)
//{
//	cout << "double Add(double left, double right)" << endl;
//	return left + right;
//}
////参数个数不同
//void f()
//{
//	cout << "void f()" << endl;
//}
//void f(int a)
//{
//	cout << "void f(int a)" << endl;
//}
////参数顺序不同
////当函数参数中存在缺省的情况下,在调用函数时,因缺省参数导致传入参数与构成重载函数的参数列表相同时,编译器会进行报错,因为不能进行区分调用的函数
////参数个数,类型,顺序均相同的情况下,缺省值不同不能构成函数重载
//void fun(int a = 1, char b = 2)			//可以构成重载,但调用时传入的参数使重载函数都可以调用时,会进行报错
//{
//	cout << "void f(int a, char b)" << endl;
//}
//void fun(char b = 2, int a = 1)
//{
//	cout << "void fun(char b, int a)" << endl;
//}
//int main()
//{
//	Add(1, 2);
//	Add(1.0, 2.0);
//
//	f();
//	f(1);
//	
//	fun(10, 'A');
//	fun('A', 10);
//
//	//fun();	//因缺省参数导致传入参数与构成重载函数的参数列表相同时,编译器会进行报错
//
//	return 0;
//}

//引用
//引用不是新定义一个变量,而是给已存在的变量取一个别名
//编译器不会为引用变量开辟空间,变量和其引用的变量共同使用一块内存空间
//使用方式:		类型& 引用变量名 = 引用实体
//引用与指针的不用:
//		1.	概念上		引用定义了一个变量的别名				指针存储了一个变量的地址
//		2.	引用在定义时必须进行初始化				指针无特殊要求
//		3.	引用在初始化引用一个实体后就不能		指针可以任意指向同类型的实体
//			再引用其他实体
//		4.	没有NULL引用							有NULL指针
//		5.	sizeof中	引用为引用类型的大小					指针是地址空间所占字节数
//		6.	引用自加为引用实体+1					指针自加为向后偏移一个类型大小			
//		7.	没有多级引用							有多级指针	
//		8.	引用访问实体编译器自己处理				指针访问实体需要显式解引用
//		9.	引用比指针的使用更加安全
//int main()
//{
//	int a = 10;
//	cout << "&a " << &a << endl;		//000000688CDAFB74
//	
//	//&的作用:	引用定义
//	int& ra = a;
//	cout << ra << endl;
//	cout << "&ra " << &ra << endl;		//000000688CDAFB74
//
//	//&的作用:	对变量进行取地址
//	int* pa = &a;
//	cout << *pa << endl;
//	cout << "&pa " << &pa << endl;		//000000688CDAFBB8
//
//	//引用必须在定义的时候进行初始化
//	//int& b;	//报错
//
//	//一个变量可以有多个引用
//	int& b = a;
//	int& c = a;
//	int& d = a;
//	//引用也可以有引用
//	int& e = d;
//	cout << e << endl;
//
//	//一个引用一旦引用一个实体,就再也不能引用其他实体
//	//又根据引用的特性,在创建一个引用的时候必须进行赋值,故一个引用的整个生命周期都只能引用一个实体
//	int x = 20;
//	//可能有两重含义
//	//1. 让 b 变成 c 的引用
//	//2. 让 c 赋值给 b
//	//为了防止歧义,取消引用变成其他变量引用的能力
//	b = x;
//
//	return 0;
//}
//引用的作用场景
//引用作为参数
//在参数相同的情况下,传引用,传址,传值,三种方式构成函数重载
//但在调用时,传值与传引用存在调用歧义
//void Swap(int& op1, int& op2)
//{	//传引用
//	int tmp = op1;
//	op1 = op2;
//	op2 = tmp;
//}
//void Swap(int* op1, int* op2)
//{	//传址
//	int tmp = *op1;
//	*op1 = *op2;
//	*op2 = tmp;
//}
////void Swap(int op1, int op2)
////{	//传值
////	int tmp = op1;
////	op1 = op2;
////	op2 = tmp;
////}
////引用作返回值
////如果函数返回时,出了函数作用域,如果返回对象还在(还没还给操作系统),则可以使用引用返回
////如果已经还给了操作系统,则必须使用传值返回
//int& Add(int a, int b)
//{	//引用返回的含义:	不会生成临时拷贝返回,而是直接返回返回值的引用
//	//存在非法访问,返回值是引用,在函数栈帧销毁之后,又回去访问了该位置的空间
//	//若函数栈帧销毁之后,清理空间,在取返回值的时候可能会读取到随机值
//	int c = a + b;
//	cout << c << ' ';
//	cout << &c << endl;
//	return c;
//}
//int main()
//{
//	//引用作为参数
//	int x = 0, y = 1;
//	cout << x << ' ' << y << endl;
//	Swap(x, y);
//	cout << "Swap" << endl;
//	cout << x << ' ' << y << endl;
//	cout << endl;
//
//	//引用作为返回值
//	int ret = Add(1, 2);
//	cout << ret << ' ';			//3
//	cout << &ret << endl;		//在main函数中的地址
//	
//	int& ret1 = Add(1, 2);
//	cout << ret1 << ' ';		//3
//	cout << &ret1 << endl;		//在调用函数中的地址
//
//	Add(10, 20);
//	cout << ret1 << endl;		//30
//
//	printf("test\n");
//	cout << ret1 << endl;		//随机值
//	//引用的作用主要体现在传参和传返回值
//	//引用传参和传返回值,有些场景下面,可以提高性能(大对象+深拷贝对象)
//	//					 输出型参数和输出型返回值,有些场景下面,形参的改变可以改变实参
//	//有些场景下面,引用返回,可以改变返回对象
//
//	//引用的时候,权限可以缩小,不变	但不能放大 
//	const int a = 10;		//a只读
//	//int& b = a;			//报错,b可读可写,权限放大
//	const int& b = a;		//权限不变
//
//	int c = 10;				//c可读可写
//	const int& d = c;		//可以,b只读,权限缩小
//
//	//假设函数参数是一个大对象或者深拷贝对象,尽量使用引用传参,减少拷贝
//	//若函数中不改变参数,尽量使用const引用传参
//
//	double dd = 1.11;
//	//在类型不同时,进行赋值发生隐式类型转换,会中间生成一个临时变量,该临时变量具有常属性,是右值
//	//生成的 临时变量的数据类型 是 被赋值的变量的数据类型
//	//先将 赋值变量 赋给 临时变量,再将 临时变量 赋给 被赋值的变量
//	int ii = dd;
//	//生成的临时变量的类型为int		先将 dd 赋给 临时变量,再将 临时变量 赋给 i
//	
//	//int& rii = dd;	//报错,将dd赋给rii引用时,生成的临时变量的类型为const int,在对临时变量进行引用时,会有权限放大的问题
//	const int& rii = dd;		//应使用const int&类型进行引用
//	//const Type&	可以接受各种类型的对象
//
//	int t = 0;
//	//在语法层面上,引用是取别名,不开辟空间
//	int& ra = t;		//lea eax, [t]
//						//mov dword ptr[ra], eax
//	ra = 20;			//mov eax, dword ptr[ra]
//						//mov dword ptr[eax], 14h
//						
//	//在语法层面上,指针是存储地址,开辟空间
//	int* pa = &t;		//lea eax, [t]
//						//mov dword ptr[pa], eax
//	*pa = 20;			//mov eax, dword ptr[ra]
//						//mov dword ptr[eax], 14h
//	//但在底层汇编指令实现来看,引用是类似指针的方式实现的
//	//在底层是会开辟空间的
//	return 0;
//}

//auto关键字	(C++11语法)
//随着程序的越来越复杂,程序类型也越来越复杂
//产生出了两大缺陷:		类型难于拼写	含义不明确容易出错
//也可以使用 typedef 重命名类型
//但会出现含义不明确的问题
//int main()
//{
//	typedef char* pst;
//	const pst p1 = NULL;			//类型为 char* const
//	const pst* p2 = NULL;			//类型为 char* const *
//	//cout << p1 << " " << p2 << endl;
//	return 0;
//
//}
//在将表达式的值赋给变量的时候,要求在声明变量时必须清楚标明表达式的类型
//故 C++11 为auto赋予了新含义
//在使用auto类型定义变量时,auto会根据右侧表达式进行自动推导变量类型
//在 C 中,使用auto修饰的变量,是具有自动存储器的局部变量
//在 C++11 中,auto不再是一个存储类型指示符
//			  而是作为一个新的类型指示符,auto声明的变量必须由编译器 在编译阶段 推导出
//			  auto变量在编译阶段编译器需要根据初始化表达式来推到类型
//auto并非是一种类型的声明,而是一个类型声明时的占位符
//编译器会在编译阶段将auto替换为变量实际的类型
//故 auto类型的变量在定义的时候,必须进行初始化
//为了避免与之前auto的概念冲突,C++11只保留了auto作为类型指示符的用法
////void Test(auto x)			//auto不能作为形参类型,编译器无法对形参的类型进行推导
//auto Test(int x1)			//auto可以作为函数返回值
//{
//	double x = 0.0;
//	return x;
//}
//int main()
//{
//	auto t = 1.1;		//t为double类型
//	//typeid(变量).name() 返回一个类型名的字符串
//	cout << typeid(t).name() << endl;
//
//	int a = 1;
//	auto b = a;			//b为int类型
//	cout << typeid(b).name() << endl;
//
//	//auto c;			//使用auto定义变量时,必须进行初始化
//	
//	//auto使用规则
//	
//	//auto与引用,指针结合起来使用时
//	int x = 0;
//	//使用auto声明指针类型时, auto 与 auto* 无任何区别
//	auto px1 = &x;			//二者完全等价
//	auto* px2 = &x;
//	//使用auto声明引用类型时, auto必须加上&		auto&
//	auto& rx = x;
//
//	//在同一行使用auto定义多个变量时
//	//这些变量只能是相同类型的,否则编译器将会报错
//	auto t1 = 1, t2 = 3, t3 = 4;
//	//auto t4 = 1, t5 = 1.1;		//报错
//	//编译器实际上只对第一个类型进行推导,使用推导出的类型定义其他变量
//
//	//auto不能直接用来声明数组
//	//auto a[5] = { 1, 2, 3, 4, 5 };	//报错
//
//	return 0;
//}

//基于范围for循环	(C++11语法)
//对于一个有范围的集合而言,由编写者说明循环范围是多余且易出错的
//因此 C++11 引入基于范围的for循环
//范围for循环后的括号由 ':'(冒号) 分为两部分
//第一部分	范围内用于迭代的变量
//第二部分	表示被迭代的范围
//范围for循环迭代的范围必须是确定的
//对数组而言	数组中第一个元素到最后一个元素之间的范围
//对类而言		应该提供 begin 和 end的方法
//				begin 和 end就是for循环的范围
//注意	在进行函数传参的时候,数组会被降维成指针
////void Func(int arr[5])
////{
////	for (auto& e : arr)			//报错
////		cout << e << ' ';
////	cout << endl;
////}
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5 };
//
//	//auto e 范围内用于迭代的变量		arr 被迭代的范围
//	//auto类型可以换成其他类型进行迭代,但推荐使用auto
//	//与普通循环类似,可以使用continue,break调整,结束循环
//	for (auto e : arr)
//	//依次取数组中的数据 赋值给 e ,自动迭代,判断,结束循环
//		cout << e << ' ';
//	cout << endl;
//	//auto&时才可以对其内容进行修改
//	for (auto& e : arr)
//		cout << (e *= 2) << ' ';
//	cout << endl;
//
//	return 0;
//}

//内联函数
//以inline修饰的函数叫做内联函数
//编译时,C++编译器会在调用函数的地方将其展开
//内联函数,没有建立栈帧的内存开销,提高程序效率
//宏函数也可以做到类似操作
//宏函数	优点: 无需建立栈帧,提高调用效率
//			缺点: 复杂,容易出错,可读性差,不能调试
//内联函数	优点: 无需建立栈帧,提高调用效率,不复杂,不易出错,可读性强,可以调试
//			缺点: 只适用于短小的频繁调用的函数,可能会导致代码膨胀
//无论是宏函数还是内联函数都只适用于短小的频繁调用的函数
//当函数过长时,编译后会导致代码膨胀,生成的可执行程序变大
//正常函数被调用时,只需一份函数体,每次调用只需call跳转至函数体,执行指令即可
//宏函数和内联函数被调用时,会在被调用处替换展开
//inline对于编译器仅仅只是一个建议,是否将函数进行内联由编译器进行决定
//不会进行内联的函数
//	1:	比较长的函数(一般编译器都是限制为5行以内)
//	2:	递归函数
//默认Debug版本下,内联不会起作用,否则不方便调试
//内联函数不会进入符号表,不会生成地址,故 声明与定义不能分离
//因为inline函数不需要被call,故不会生成地址
//若要在一个文件中定义inline函数供多个文件使用时,只能直接写在 .h 文件中(声明定义不分离)
//可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
//inline函数会在调用的地方展开，所以符号表中不会有inline函数的符号名，不存在链接冲突
//inline函数定义声明分离时,调用函数会发生链接问题
//#define _Add(x, y) (((x) + (y)) * 10)
//inline int Add(int x, int y)
//{	//编译期间,编译器会用函数体替换函数调用
//	return (x + y) * 10;
//}
//inline double Add(double x, double y)
//{	//较长时,加上内联也不会成为内联,编译器不会通过
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	cout << "inline double Add(double x, double y)" << endl;
//	return x + y;
//}
//int main()
//{
//	for (int i = 0; i < 10000; i++)
//		cout << Add(i, i + 1) << endl;
//	return 0;
//}

//nullptr (C++11)
//在良好的C/C++编程中,声明一个变量时最好是给变量一个合适的初始值
//在创建一个指针变量时通常会将其置为NULL
//NULL实质上是一个宏,在传统的C头文件(stddef.h)中
//#ifndef NULL		//若没有定义NULL
//#ifdef __cplusplus		//若定义了 __cplusplus
//#define NULL 0
//#else
//#define NULL ((void*)0)
//#endif
//#endif
//可以看出在C++98中,NULL被定义成为字面常量0
//在C中,NULL被定义成为 void* 0
//C++11中引入了一个关键字	nullptr
//在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr为关键字
//sizeof(nullptr) sizeof((void*)0)所占字节数相同
//建议后续表示指针空值时,使用nullptr
//void f(int)		//C/C++的编译器并没有规定必须要形参接收,可以通过参数去进行匹配函数,传入的参数可以不接收
//{
//	cout << "f(int)" << endl;
//}
//void f(int*)
//{
//	cout << "f(int*)" << endl;
//}
//int main()
//{
//	//C++98中的指针空值
//	//当一个指针没有合法的指向时,基本都是如下进行初始化
//	int* p1 = NULL;
//	int* p2 = 0;
//
//	f(0);			//f(int)
//	f(NULL);		//f(int)
//	f((int*)0);		//f(int*)
//	f(nullptr);		//f(int*)
//
//	return 0;
//}

//类和对象--前篇
//面向过程
//C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题
//面向对象
//C++是基于面向对象的,关注的是对象,将一件事情拆解成不同的对象,通过对象之间的交互完成
//类
//C语言结构体中只能定义变量,但在C++中,结构体内不仅可以定义变量,也可以定义函数
//C++兼容C语言,struct之前的用法仍可以使用
//struct Stack
//{
//	//类中的成员变量可以写在内部的任何位置,类域是一个整体
//
//	//成员函数
//	void Init(int defaultCapacity = 10)
//	{
//		a = (int*)calloc(defaultCapacity, sizeof(int));
//		top = -1;
//		capacity = defaultCapacity;
//	}
//
//	void Push(int x)
//	{
//		a[++top] = x;
//	}
//
//	void Destroy()
//	{
//		free(a);
//		a = nullptr;
//		top = -1;
//		capacity = 0;
//	}
//
//	//成员变量
//	int* a;
//	int top;
//	int capacity;
//};
//int main()
//{
//	struct Stack st1 = { 0 };
//	
//	Stack st = { 0 };
//	//struct调用其内部的函数时,与使用内部成员方式相同
//	st.Init();
//	st.Push(1);
//	st.Destroy();
//
//	return 0;
//}
//类的定义
//class 定义类的关键字,类定义结束后,分号不能省略
//class s
//{
//	//类体
//};
//类体的内容称为类的成员	类中变量称为 类的属性/成员变量
//							类中函数称为 类的方法/成员函数
//类的两种定义方式
//	成员函数声明,定义全部都在类中(若成员函数在类中定义,编译器可能会当作内联函数处理)
//	类声明在.h中,成员函数在.cpp中(成员函数名前需要加类名)
//一般情况下,推荐使用声明,定义相分离
//class s
//{
//	int _year;
//};
//
//类的访问限定符及封装
//访问限定符
//C++实现封装的方式:用类将对象的属性与方法结合在一块,使对象更加完善
//通过访问权限选择性的将其接口提供给外部的用户使用
//访问限定符	public		(公有)
//				protected	(保护)
//				private		(私有)
//访问限定符说明
//	public修饰的成员在类外可以直接被访问
//	protected和private修饰的成员在类外不能直接被访问
//	访问权限作用域从该访问限制符出现的位置开始直到下一个访问限定符出现时为止,若后面无访问限定符,作用域就到},即类结束
//	class的默认访问权限为private	struct的默认访问权限为public(因为struct要兼容C语言)
//	访问限定符只在编译阶段有效,当数据映射到内存中后,无任何访问限定符上的区别
//class Stack
//{
//public:			//访问限定符
//	//类的方法一般都是共有的
//	void Init(int defaultCapacity = 10);
//private:		//定义一个类,数据变量一般都是私有/保护的
//	int* a;
//	int top;
//	int capacity;
//};
////类的声明与成员函数的定义分离
//void Stack::Init(int defaultCapacity)
//{	//成员函数名前加类名标识
//	a = (int*)calloc(defaultCapacity, sizeof(int));
//	top = -1;
//	capacity = defaultCapacity;
//}
//
//封装
//面向对象的三大特性	封装	继承	多态
//封装:	将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,进对外公开接口和对象进行交互
//本质上是一种管理,让用户更方便的使用类
//通过访问权限来隐藏对象内部的实现细节,控制部分方法可以在类外被直接使用
//类的作用域
//类定义了一个新的作用域	类域
//类的所有成员都在类域中
//类体外定义成员时,需要使用 :: 作用域操作符指明成员属于的类域
//类中变量的搜索顺序	局部域	类域	全局域
//类的实例化
//用类类型创建对象的过程,称为类的实例化
//	类是对对象进行描述的,类似于模型,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间去存储	
//	一个类可以实例化出多个对象,实例化出的对象占有实际的物理空间,存储类成员变量
//
// 类的大小
//一个类的大小,实际就是该类中所有成员变量的大小(与结构体类似,存在内存对齐)
//当遇到空类时,编译器会给空类一个字节来唯一标识这个类的对象
//类对象的存储方式:	只保留成员变量,成员函数存放在公共的代码段中
//一个类可以实例化出多个对象	其每个对象只将内部的成员变量进行存储
//								而类成员函数则放在了公共代码区
//一个类的不同对象调用相同的成员函数时,调用的为同一地址同一份代码
//int main()
//{
//	Stack st1;		//类实例化对象,对象的定义
//	cout << sizeof(st1) << endl;		//16
//}
//
//this指针
//当一个类实例化出多个对象后,不同的对象调用成员函数时是访问同一个函数地址
//当不同的对象实际调用成员函数时,成员函数如何得知是哪个对象的调用
//为了解决该问题,C++引入了this指针(关键字)
//编译器为每一个 非静态的成员函数 增加了一个隐藏的指针参数
//当调用该函数时,会自动调用该函数的对象传参(让this指针指向当前调用函数的对象)
//函数体中的所有成员变量均是通过该指针访问的
//但所有的操作对于对象来说都是透明的,无需用户传递编译器自动完成
//this指针的特性
//	this指针的类型:	类的类型* const 在成员函数中,this指针不允许被赋值
//	this只能在成员函数内部被使用
//	this指针本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参
//					this指针在栈中存储(同普通的参数一样,作为函数栈帧的一部分),对象中不存储this指针
//	this指针是成员函数 第一个 隐含的指针形参
//	一般情况下,由编译器通过ecx寄存器自动传递,无需用户操作
//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	void Print()		//编译器会对成员函数自动省略this指针参数,在编译后会自动填补
//	//void Print(Data* const this)		//this为关键字,不能作为函数参数名
//	{
//		//编译器会将其处理成 this指针指向的访问
//		//编译器允许this在内部显示使用,但不允许作为形参和实参显示传递
//		//this不允许被修改,其 this指针 本身被const修饰
//		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
//		//cout << _year << "-" << _month << "-" << _day << endl;
//	}
//
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//int main()
//{
//	Date d1, d2;
//	
//	d1.Init(2022, 1, 11);
//	d2.Init(2022, 1, 12);
//
//	d1.Print();			//而在调用处,编译器会自动传入d1的地址
//	d2.Print();
//	
//	return 0;
//}

//类和对象--中篇
//类的默认成员函数
//一个类中没有任何成员函数时,称为空类
//任何类在什么都不写时,编译器会自动生成以下6个默认成员函数
//默认成员函数:	用户无显示实现,编译器为其生成的成员函数
//		初始化与清理	构造函数主要完成初始化工作
//						析构函数主要完成清理工作
//		拷贝复制		拷贝构造是使用同类对象初始化创建对象
//						赋值重载主要是把一个对象赋值给另一个对象
//		取地址重载		主要是普通对象和const对象取地址
//							这两个函数一般很少由用户实现
//
//构造函数		一个特殊的成员函数
//名字与类名相同,创建类类型对象时,由编译器自动调用
//若类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(用户显示定义后,编译器将不再生成)
//以保障每个数据成员都有一个合适的初始值(在对象整个生命周期内只调用一次)
//构造函数虽然名叫构造,但构造函数的主要作用不是开辟空间创建对象,而是初始化对象
//特性
//		函数名与类名相同
//		无返回值	(不用写返回值,也不会返回值)
//		对象实例化时编译器自动调用对应的构造函数
//		构造函数可以重载
//语法上,构造函数可以时private,但一般都是public
//C++将类型分为:	内置类型/基本类型	语言本身定义的基础类型 int/char/double/指针
//					自定义类型			用struct,class等定义的类型
//一般情况下	有内置类型成员,就要自己写构造函数,不能使用编译器自动生成的函数
//				但若是全都为自定义类型,则可以考虑使用编译器自动生成的
//      若在构造函数的初始化列表中没有对自定义类型的显式初始化,编译器就会自动调用其自身的默认构造函数
//C++11为了解决内置类型成员不能初始化的缺陷
//内置类型成员变量在类中声明时可以给默认值(缺省值)	不是初始化
//													是给编译器自动生成的初始化列表使用的
//默认构造函数	(不传参就可以进行调用的函数)
//编译器自动生成的构造函数	无参构造函数	带参全缺省构造函数(类中默认构造函数有且只能有一个)
//调用默认构造函数时,不能使用函数操作符(),否则会与一般的函数声明出现歧义
//类名 变量;  调用无参构造函数
//类名 变量(参数列表);	调用带参构造函数
//
//析构函数		与构造函数功能相反
//析构函数不是完成对对象本身的销毁,局部对象的销毁工作是由编译器完成
//析构函数没有返回值且没有参数,函数重载的条件为:  参数不同,故析构函数不能进行重载
//对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
//用户没有显示定义析构函数时,编译器会自动生成默认析构函数
//编译器生成的默认析构函数	内置类型成员不做处理
//							自定义类型成员调用其自己的析构函数
//析构函数会自动调用自定义类型成员自身的析构函数,无需用户手动调用其析构函数
//特性		析构函数是特殊的成员函数
//		析构函数名是在类名前加字符 `
//		无参数,无返回值类型
//		一个类只能有一个析构函数,若未显示定义,系统会自动生成默认的析构函数
//		析构函数不能重载
//		对象生命周期结束时,C++编译器自动调用析构函数
//一般情况下	有动态申请的资源都需要用户去显示定义析构函数
//				没有动态申请的资源,可以不用写析构函数
//				需要释放资源的成员都是自定义类型,也可以不用写析构函数
//析构函数的调用销毁对象的顺序
//在同一声明周期内,先创建的对象后销毁,同栈的使用规则一样(FIFO)
//销毁优先级:	局部 > (返回值) > 静态 > 全局
//
//拷贝构造函数	 (本质是构造函数)
//只有单个形参,该形参是本类类型对象的引用(一般使用const进行修饰)
//				在用已存在的类类型对象创建新对象时由编译器自动调用
//特征:		拷贝构造函数是特殊的成员函数
//		拷贝构造函数是构造函数的一个重载形式
//		参数只能有一个,且必须是类类型对象的引用(传值方式时,编译器直接报错)
//传值调用函数时,其本身就会发生拷贝,而类的拷贝又要调用该函数,故会引发无穷递归调用
//若未显示定义,编译器会自动生成默认的拷贝构造函数
//				默认的拷贝构造拷贝对象	内置类型按内存存储以字节序完成拷贝(值拷贝/浅拷贝)
//										自定义类型是调用其自身的拷贝构造函数
//C++中规定:自定义类型的拷贝必须调用拷贝构造函数(赋值/函数传参也是一种拷贝)
//			内置类型直接拷贝即可
//一般情况下	类中没有涉及资源申请时,拷贝构造函数可以不用显示定义
//				一旦涉及到资源申请的,一定要用户显示定义拷贝构造函数(否则就是浅拷贝)
//				需要释放资源的成员都是自定义类型,也可以不用写拷贝构造函数
//拷贝构造函数调用场景
//			使用已经存在的对象创建新对象
//			函数参数类型为类类型对象
//			函数返回值类型为类类型对象
//
//运算符重载
//C++是默认不支持自定义类型使用运算符的
//C++为了增强代码的可读性,引入了运算符重载
//运算符重载是具有特殊函数名的函数		具有其返回值类型(看操作符运算后返回值的类型)
//										函数名称以及参数列表
//										其返回值类型与参数列表与普通函数类似
//C中不写函数返回值时,默认为int,但C++中必须写函数返回值,否则就会报错
//若类中和全局中都定义有一个运算符重载的函数,且两个函数之间构成重载时,一般编译器都会优先调用类中的运算符重载的函数
//函数名称为:	关键字 operator + 重载的运算符符号
//函数原型:		返回值类型 operator操作符(参数列表)
//注意
//			不能通过连接其他符号来创建新的操作符
//			重载操作符必须有一个类类型对象
//			用于内置类型的运算符,其含义不能重载改变
//			作为类成员函数重载时,其形参看起来比操作数数目少一(其成员函数的第一个参数为隐藏的this)
//			.*	::	sizeof	?:	.	以上5个运算符不能进行重载
//		//:: 作用域访问符	sizeof 计算大小		?: 三目操作符		. 类访问成员
//运算符重载成全局时,就需要成员变量是公有的,故建议重载成类的成员函数
//运算符重载时,几个操作数就有几个参数(作为类的成员函数时,会将第一个参数this隐藏)
//								且参数不能是内置类型
//
//赋值运算符重载		(本质是运算符重载函数)
//重载格式	参数类型:	const T& 传递引用可以提高传参效率
//			返回值传参:	T& 返回引用可以提高1返回的效率,有返回值的目的是支持连续赋值
//			检测是否为自己给自己赋值
//			返回 *this
//赋值运算符重载函数只能重载为类的成员函数,不能重载成全局函数
//											若要重载成全局函数时,需要手动添加参数取代this
//赋值运算符重载函数如果没有在类里显示定义,编译器就会自动生成一个默认的赋值运算符重载
//用户在类外自己实现的全局赋值运算符重载就会与编译器自动生成的产生冲突
//编译器生成的默认赋值运算符重载:	以值的方式逐字节拷贝(与拷贝构造函数的行为是相同的)
//								内置类型成员变量直接赋值
//								自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值
//
//前置++与后置++重载
// 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
//C++规定:	后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
//前置++返回+1后的结果,可以直接使*this+=1后返回*this	返回类型为 类&
//后置++是返回+1之前的旧值,之后使*this+=1,故先将this保存一份,然后使this+=1,返回保存的this备份	返回类型为 类
//前置--与后置--同++的重载方式相同
//
//const 成员
//将const修饰的成员函数称为 const成员函数
//const修饰对象成员函数,实际修饰的是,该成员函数隐含的this指针 (this指针: 类* const this,其本身拥有的const属性是修饰this指针本身的)
//表明在该成员函数中不能对类中的任何成员进行修改
//const修饰成员函数后,const 类* this(该const修饰*this) --> const 类* const this(this指针本身指向不可改变,this指针指向内容不可改变)
//const成员仅修饰this指针,即类中成员不可改变
//一般来说,能加const修饰的成员函数一般都要加上const进行修饰,以使得普通对象和const对象都可以调用该函数
//		   但如果要修改成员变量的函数就不能加上const
//声明和定义分离时,声明和定义都要标识const
//
//取地址及const取地址操作符重载
//这两个默认成员函数一般不必是用户显示定义,编译器默认生成即可
//只有特殊情况下,如让用户获取到指定地址的内容等,才需要用户显式定义
//一般情况下,返回值均为类* 返回的均是this
//取地址重载:		类* operator()				实际参数为:	类* const this
//const取地址重载:	const 类* operator() const	实际参数为:	const 类* const this
//二者之间参数类型不同,会构成函数重载
//
//void Func(Date d)
//{
//	cout << "Func" << endl;
//	//函数运行结束时,也会自动调用析构函数
//}
//int main()
//{
//	Date d1;
//	d1.DateShow();
//	Func(d1);		//调用函数传参时,也会调用拷贝构造函数
//	return 0;
//}
//
//重载<<(流插入),>>(流提取)操作符
// << 流插入操作符的两个操作数,一个是cout(一个全局类型的对象),一个是类型
//									cout的类型为ostream(输出流类,一个类) cin的类型为istream(输入流类)
//流插入的自动识别类型的实质是函数重载
//例:	ostream& operator<<(int val) //省略this参数, ostream* const this (*this为cout)
//但在类中实现的<<(流插入),>>(流提取)操作符重载的使用与C++正常使用习惯不同
//		类中默认第一个参数为隐藏的this指针,而对于 双操作数的 操作符重载函数 来说 左操作数为第一个参数,右操作数为第二个参数
//类中定义的使用方式为:		对象 << cout;		对象.operator<<(cout);
//故一般都会在全局中实现重载,利用友元访问类中的私有成员变量
//
//class Date
//{
//public:
//	//构造函数名与类名相同,不能写返回值,且可以进行函数重载
//	Date(int year = 1, int month = 1, int day = 1)
//	{	//对象实例化时自动调用对对象进行初始化
//		cout << "Date()" << endl;
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	//拷贝构造函数(也是构造函数)
//	Date(const Date& sour)		//参数有且只能有一个,且只能是本类类型对象的引用(const修饰的引用)
//	{
//		cout << "DateCopy" << endl;
//		_year = sour._year;
//		_month = sour._month;
//		_day = sour._day;
//	}
//
//	void DateShow() const		//修饰*this,保护类中成员不会被修改
//	{
//		cout << _year << '-' << _month << '-' << _day << endl;
//	}
//
//	//运算符重载函数
//	bool operator<(const Date& d)		//该操作符有几个操作数就有几个参数(第一个参数为隐藏参数this, Date* const this)
//	{
//		if (_year < d._year)
//			return true;
//		else if (_year == d._year && _month < d._month)
//			return true;
//		else if (_year == d._year && _month == d._month && _day < d._day)
//			return true;
//		return false;
//	}
//
//	//赋值运算符重载
//	//返回值为 *this,返回值类型为 类型&
//	Date& operator=(const Date& d)		//参数类型为 const 类型&
//	{
//		if (this != &d)		//要检测是否为自己给自己赋值
//		{
//			_year = d._year;
//			_month = d._month;
//			_day = d._day;
//		}
//		return *this;
//	}
//
//	bool LeapYear(int year)
//	{
//		return ((!(year % 4) && year % 100) || !(year % 400));
//	}
//	int GetMonthDay(int year, int month)
//	{
//		static const int daysArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//		return daysArr[month] + (month == 2 && LeapYear(year));
//	}
//	Date& operator+=(int days)
//	{
//		_day += days;
//		while (_day > GetMonthDay(_year, _month))
//		{
//			_day -= GetMonthDay(_year, _month);
//			_month++;
//			if (_month > 12)
//				_year++, _month = 1;
//		}
//		return *this;
//	}
//	//编译器为了区分前置++与后置++的区别,牺牲了后置++,后置++重载时,需要添加一个int参数
//	//但实际调用时,无需用户手动传参,编译器自动传递参数
//	Date& operator++()		//前置++重载
//	{
//		*this += 1;			//this虽然是重载函数中的临时变量,但*this不是,故可以通过传引用返回
//		return *this;
//	}
//	Date operator++(int)	//后置++重载
//	{
//		Date ret(*this);		//ret为重载函数中的临时变量,运行完之后会调用销毁,故只能传值返回
//		*this += 1;
//		return ret;
//	}
//
//	//使用不便一般不在类内定义
//	ostream& operator<<(ostream& out)		// >> 流插入操作符重载
//	{					//将cout(全局)传入
//		out << _year << '/' << _month << '/' << _day << endl;
//		return out;
//	}
//	istream& operator>>(istream in)		// << 流提取操作符重载	
//	{					//将cin(全局)传入
//		in >> _year;
//		in >> _month;
//		in >> _day;
//		return in;
//	}
//	//在全局定义,利用友元访问类中的成员
//	friend ostream& operator<<(ostream& out, const Date& x);
//	friend istream& operator>>(istream& out, Date& x);
//
//	Date* operator&()		//取地址重载
//	{
//		return this;
//	}
//
//	const Date* operator&() const		//const取地址重载
//	{
//		return this;
//	}
//
//	//析构函数名是在类名前加' ~ ',无参数,无返回值类型
//	~Date()			//不能进行函数重载,一个类只能拥有一个析构函数
//	{	//对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
//		cout << "~Date()" << endl;
//		_year = 0;
//		_month = 0;
//		_day = 0;
//	}
//private:
//	//可以对内置类型成员给默认值
//	int _year;
//	int _month;
//	int _day;
//};
//ostream& operator<<(ostream& out, const Date& x)
//{
//	out << x._year << '/' << x._month << '/' << x._day;
//	return out;
//}
//istream& operator>>(istream& in, Date& x)
//{
//	in >> x._year;
//	in >> x._month;
//	in >> x._day;
//	return in;
//}
//int main()
//{
//	Date d1(2023, 4, 28);
//	Date d2(2023, 4, 29);
//
//	cout << (d1 < d2) << endl;		//d1 < d2 会被转换成 d1.operator<(d2)
//	cout << d1.operator<(d2) << endl;
//	
//	//拷贝构造 用一个已经存在的对象初始化另一个对象		(本质是一个运算符重载)
//	Date d3(d1);
//	Date d4 = d3;		//调用构造函数,等价于 Date d4(d3);
// 
//	//赋值重载 已经存在的两个对象之间的复制拷贝		(本质是一个构造函数)
//	//赋值重载只有在两个已经存在的对象之间才会是复制拷贝
//	d1 = d2 = d3;		//有返回值的目的是支持连续赋值
//
//	//const修饰的对象只能调用const成员
//	const Date _d1(d1);
//	d1.DateShow();
//	cout << endl;
//
//	//cout << d1;		//报错,传参顺序为:	传左操作数为第一个参数,右操作数为第二个参数
//						//但实质上第一个参数为 Date类的this指针
//	d1.operator<<(cout);
//	d1 << cout;			//不适应于正常的C++语言的使用习惯
//	//全局重载的流操作符与正常的C++语法的使用习惯是相同的
//	cin >> d1;
//	cout << d1 << endl;
//}
//
//匿名对象
//匿名对象的属性为const常属性,其生命周期在当前语句中
//但当有const引用 引用 匿名对象时,其生命周期延长,变为 const引用 的生命周期
//创建匿名对象的方式:	类()
//int main()
//{
//	Date x;		//普通对象的生命周期是在其定义的域中
//	Date();		//匿名对象的生命周期是在当前语句中
//	//使用场景,只想要调用类中的函数(调用一次),或者只是用于一次传参时使用
//	Date(2022, 1, 17).DateShow();
//
//	//被 const引用 引用后,匿名对象的生命周期改变
//	const Date& rx = Date();
//	rx.DateShow();
//
//	return 0;
//}
//
//日期类的构造
//class Date
//{
//	friend ostream& operator<<(ostream& _cout, const Date& d);
//	friend istream& operator>>(istream& _cin, Date& d);
//public:
//
//	static bool LeapYear(int year);								//判断是否为闰年
//	static int GetMonthDay(int year, int month);				//得到一个月的天数
//
//	static int GetWeekDay(int year, int month, int day);		//获取该日期是星期几
//	Date(int year = 1900, int month = 1, int day = 1);			//默认构造函数
//	Date(const Date& x);										//拷贝构造函数
//
//	void DateShow() const;										//日期显示函数
//
//	bool operator<(const Date& x) const;						// < 操作符重载函数
//	bool operator==(const Date& x) const;						// == 操作符重载函数
//	bool operator>(const Date& x) const;						// > 操作符重载函数
//
//	bool operator<=(const Date& x) const;						// <= 操作符重载函数
//	bool operator>=(const Date& x) const;						// >= 操作符重载函数
//	bool operator!=(const Date& x) const;						// != 操作符重载函数
//
//	Date& operator=(const Date& x);								// = 赋值操作符重载函数
//
//	Date* operator&();											//取地址重载
//	const Date* operator&() const;								//const取地址重载
//
//	static int GetYearDif(int Lyear, int Ryear);				//Lyear年1月1号到Ryear年1月1号之间的天数
//	static int GetMonthDif(int year, int Lmonth, int Rmonth);	//year年中,Lmonth月1号到Rmonth月1号之间的天数
//
//	Date& operator+=(int days);									// += 操作符重载函数
//	Date& operator-=(int days);									// -= 操作符重载函数
//
//	Date operator+(int days) const;								// + 操作符重载函数,计算days天后的日期
//	Date operator-(int days) const;								// - 操作符重载函数,计算days天前的日期
//
//	Date& operator++();											//前置++ 操作符重载函数
//	Date operator++(int);										//后置++ 操作符重载函数
//
//	Date& operator--();											//前置-- 操作符重载函数
//	Date operator--(int);										//后置-- 操作符重载函数
//
//	int operator-(const Date& x) const;							// - 操作符重载,计算两个日期类对象之间的天数
//
//	~Date();													//析构函数
//
//private:
//
//	int _year;
//	int _month;
//	int _day;
//	int _weekday;
//};
//
//bool Date::LeapYear(int year)
//{
//	return ((!(year % 4) && year % 100) || !(year % 400));
//}
//int Date::GetMonthDay(int year, int month)
//{
//	static const int daysArr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//	return daysArr[month] + (month == 2 && LeapYear(year));
//}
//int Date::GetWeekDay(int year, int month, int day)
//{
//	//根据Zeller公式计算
//	//century: 世纪-1		tyear: 世纪内年份
//	int century = year / 100, tyear = year % 100;
//	//1月,2月当作上一年的13月,14月计算,月份数值从3 -- 14
//	if (month == 1 || month == 2)
//		month += 12;
//	//0 - 星期日 1 - 星期一 2 - 星期二 3 - 星期三 4 - 星期四 5 - 星期五 6 - 星期六
//	return (century / 4 - 2 * century + tyear + tyear / 4 + 13 * (month + 1) / 5 + day - 1) % 7;
//}
//
//Date::Date(int year, int month, int day)
//{
//	_year = year;
//	_month = month;
//	_day = day;
//	if (!(_year >= 0 \
//		&& (_month > 0 && _month < 13) \
//		&& (_day > 0 && _day <= GetMonthDay(_year, _month))))
//	{
//		cout << "非法日期" << endl;
//		DateShow();
//	}
//	else
//		_weekday = GetWeekDay(_year, _month, _day);
//}
//Date::Date(const Date& x)
//{
//	_year = x._year;
//	_month = x._month;
//	_day = x._day;
//	_weekday = x._weekday;
//}
//
//void Date::DateShow() const
//{
//	static const char* weekday[7] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
//	cout << _year << '-' << _month << '-' << _day << ' ' << weekday[_weekday] << endl;
//}
//
//bool Date::operator<(const Date& x) const
//{
//	if (_year < x._year)
//		return true;
//	else if (_year == x._year && _month < x._month)
//		return true;
//	else if (_year == x._year && _month == x._month && _day < x._day)
//		return true;
//	return false;
//}
//bool Date::operator==(const Date& x) const
//{
//	return (_year == x._year && _month == x._month && _day == x._day);
//}
//bool Date::operator<=(const Date& x) const
//{
//	return (*this < x || *this == x);
//}
//bool Date::operator>(const Date& x) const
//{
//	return !(*this <= x);
//}
//bool Date::operator>=(const Date& x) const
//{
//	return !(*this < x);
//}
//bool Date::operator!=(const Date& x) const
//{
//	return !(*this == x);
//}
//
//Date& Date::operator+=(int days)
//{
//	if (days < 0)
//		*this -= -days;
//	else
//	{
//		_day += days;
//		while (_day > GetMonthDay(_year, _month))
//		{
//			_day -= GetMonthDay(_year, _month);
//			_month++;
//			if (_month > 12)
//				_year++, _month = 1;
//		}
//		_weekday = GetWeekDay(_year, _month, _day);
//	}
//	return *this;
//}
//Date Date::operator+(int days) const
//{
//	Date ret(*this);
//	ret += days;
//	return ret;
//}
//Date& Date::operator++()
//{
//	*this += 1;
//	return *this;
//}
//Date Date::operator++(int)
//{
//	Date ret(*this);
//	*this += 1;
//	return ret;
//}
//
//Date& Date::operator-=(int days)
//{
//	if (days < 0)
//		*this += -days;
//	else
//	{
//		_day -= days;
//		while (_day < 1)
//		{
//			if (_month == 1)
//				_year--, _month = 13;
//			_day += GetMonthDay(_year, --_month);
//		}
//		_weekday = GetWeekDay(_year, _month, _day);
//	}
//	return *this;
//}
//Date Date::operator-(int days) const
//{
//	Date ret(*this);
//	ret -= days;
//	return ret;
//}
//Date& Date::operator--()
//{
//	*this -= 1;
//	return *this;
//}
//Date Date::operator--(int)
//{
//	Date ret(*this);
//	*this -= 1;
//	return ret;
//}
//
//int Date::GetYearDif(int Lyear, int Ryear)
//{
//	char flag = Lyear > Ryear ? 1 : -1;
//	int max = 0, min = 0;
//	if (flag == 1)
//		max = Lyear, min = Ryear;
//	else
//		max = Ryear, min = Lyear;
//	static const int Days[2] = { 365, 366 };
//	int dif = 0;
//	while (min < max)
//		dif += Days[(int)LeapYear(min++)];
//	return dif * flag;
//}
//int Date::GetMonthDif(int year, int Lmonth, int Rmonth)
//{
//	char flag = Lmonth > Rmonth ? 1 : -1;
//	int max = 0, min = 0;
//	if (flag == 1)
//		max = Lmonth, min = Rmonth;
//	else
//		max = Rmonth, min = Lmonth;
//	int dif = 0;
//	while (min < max)
//		dif += GetMonthDay(year, min++);
//	return dif * flag;
//}
//int Date::operator-(const Date& x) const
//{
//	//将日期分为四段	一段是从 *this 年的1月1号 - x 年的1月1号
//	//					一段是从 *this 年的1月1号 - *this 年的 *this 月
//	//					一段是从 x 年的1月1号 - x年的 x 月
//	//					一段是从 同年同月的 日期差
//	//两个日期的相差为:	第一段 + 第二段 - 第三段 + 第四段
//	return GetYearDif(_year, x._year) \
//		+ GetMonthDif(_year, _month, 1) \
//		- GetMonthDif(x._year, x._month, 1) \
//		+ (_day - x._day);
//}
//
//Date& Date::operator=(const Date& x)
//{
//	if (this != &x)
//	{
//		_year = x._year;
//		_month = x._month;
//		_day = x._day;
//		_weekday = x._weekday;
//	}
//	return *this;
//}
//
//Date* Date::operator&()
//{
//	return this;
//}
//const Date* Date::operator&() const
//{
//	return this;
//}
//
//Date::~Date()
//{
//	_year = -1;
//	_month = -1;
//	_day = -1;
//	_weekday = -1;
//}
//
//ostream& operator<<(ostream& _cout, const Date& x)
//{
//	_cout << x._year << "-" << x._month << "-" << x._day;
//	return _cout;
//}
//istream& operator>>(istream& _cin, Date& x)
//{
//	_cin >> x._year;
//	_cin >> x._month;
//	_cin >> x._day;
//	return _cin;
//}
//
//void TestDate1()
//{
//	Date d1(2023, 4, 28);
//	cout << "+" << endl;
//	d1 + 1;
//	d1.DateShow();
//	d1 += 1;
//	d1.DateShow();
//	d1 = d1 + 1;
//	d1.DateShow();
//	d1 = d1 += 1;
//	d1.DateShow();
//	cout << "-" << endl;
//	d1 - 1;
//	d1.DateShow();
//	d1 -= 1;
//	d1.DateShow();
//	d1 = d1 - 1;
//	d1.DateShow();
//	d1 = d1 -= 1;
//	d1.DateShow();
//}
//
//void TestDate2()
//{
//	Date d1(2023, 4, 30);
//	Date d2(2023, 4, 30);
//	(d2 -= -2).DateShow();
//	cout << (d2 - d1) << endl;
//	(d1 += -2).DateShow();
//	cout << (d1 - d2) << endl;
//	cin >> d1;
//	cout << d1 << endl;
//}
//
//int main()
//{
//	TestDate1();
//	cout << endl;
//	TestDate2();
//	return 0;
//}

//类和对象--下篇
//友元
//友元提供了一种突破封装的方式
//但友元会增加耦合度,破坏了封装,不宜多用
//友元分为:	友元函数	友元类
//
//友元函数
//重载流操作符时,cout/cin对象与隐含的this指针位置相互冲突
//this指针默认是第一个参数,即左操作数
//而在实际使用中,cout/cin也是需要是第一个形参,左操作数
//故需要将其重载为全局函数
//但全局函数无法访问类中的成员,为了解决类外函数无法访问类中成员的问题
//C++引入了友元
//友元函数可以直接访问类中的私有成员,友元函数是定义在类外的普通函数,不属于任何类
//但需要在类的内部声明,声明时 + friend(C++关键字) 进行修饰
//注意		
//			友元函数可以访问类的私有和保护成员,但不属于类的成员函数
//			友元函数不能使用const进行修饰
//			友元函数可以在类中任意地方进行声明,不受类访问限定符的限制
//			一个函数可以是多个类的友元函数
//			友元函数的调用与普通函数的调用原理类似
//
//续构造函数
//构造函数体赋值
//在创建对象时,编译器通过调用构造函数,给对象中成员变量一个合适的初始值
//调用一个一般的构造函数后,对象中已经有了一个初始值,但不能将其称为初始化
//构造函数体中的语句只能将其称为赋初值
//因为初始化只能初始化一次,而构造函数体中可以多次赋值
//因此引入了初始化列表
//在构造函数的 函数首部 与 函数体之间,以一个冒号开始,接着是一个以逗号分隔的数据成员列表
//														每一个成员变量后面跟一个放在括号中的初始化值或表达式
//只用写在构造函数的定义处,声明处不能写
//注意:
//			每个成员变量都只能出现一次(初始化只能初始化一次),可以不出现,不初始化
//			类中包含 引用成员变量,const成员变量,自定义类型成员(该类没有默认的构造函数)时,必须放在初始化列表的位置进行初始化
//类 对象(参数);	对象整体的一个定义
//而对象中每一个成员的定义是在初始化列表时定义的(进入构造函数函数体后,成员变量就已经存在了)
//但引用变量与const变量都只能在定义的时候赋值,故只能将其放入初始化列表进行初始化
//自定义类型成员无默认构造函数(不需要传参即可调用的构造函数)时,也是必须在定义时进行传参调用,只能写入初始化列表
//			尽量使用初始化列表初始化自定义类型,因为不管用户是否使用初始化列表,对于自定义类型,一定会先使用初始化列表初始化
//			成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
//			对于其他类型的成员变量,可以在任意地方初始化
//一个类中,声明的顺序尽量和初始化列表出现的顺序保持一致
//
//class A
//{
//public:
//	A(int a)
//	{
//		_a = a;
//	}
//private:
//	int _a;
//};
//class Date
//{
//public:
//	//友元函数的声明,在类中使用friend关键字修饰,可以放在类中任意位置,不受类的访问限定符的限制
//	friend ostream& operator<<(ostream& out, const Date& d);
//	friend istream& operator>>(istream& out, Date& d);
//	Date(int i, int year = 1900, int month = 1, int day = 1)
//		: _N(1)
//		, _ref(i)
//		, _aa(0)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	void DateShow() const
//	{
//		cout << _year << '-' << _month << '-' << _day << endl;
//	}
//	Date& operator=(const Date& d)
//	{
//		if (this != &d)
//		{
//			_year = d._year;
//			_month = d._month;
//			_day = d._day;
//		}
//		return *this;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//
//	const int _N;
//	int& _ref;
//	A _aa;
//};
////在类中被friend修饰声明后,可以访问类中的成员
//ostream& operator<<(ostream& out, const Date& d)
//{
//	out << d._year << '/' << d._month << '/' << d._day;
//	return out;
//}
//istream& operator>>(istream& in, Date& d)
//{
//	in >> d._year;
//	in >> d._month;
//	in >> d._day;
//	return in;
//}
//int main()
//{
//	int i = 0;
//	Date d(i);
//	//从左向右结合表达式,每个表达式的返回值都是cout,作为下次表达式的左操作数cout
//	cout << "日期:" << d << endl;
//	cin >> d;
//	cout << "日期:" << d << endl;
//
//	return 0;
//}
//explicit 关键字
//构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数(即只需要传一个参数的构造函数)
//还具有自动类型转换的作用
//而使用explicit修饰构造函数,可以阻止编译器进行自动的类型转换赋值操作
//在函数定义与声明分离时,可以在任意一处写explicit,建议在声明处写
//class B
//{
//public:
//	explicit B(int b)
//		: _b(b)
//	{
//		cout << "B(int b)" << endl;
//	}
//	B(const B& b)
//	{
//		cout << "B(const B& b)" << endl;
//		_b = b._b;
//	}
//	B& operator=(const B& b)
//	{
//		cout << "B& operator=(const B& b)" << endl;
//		_b = b._b;
//		return *this;
//	}
//	~B()
//	{
//		cout << "~B()" << endl;
//	}
//private:
//	int _b;
//};
//int main()
//{
//	B b(1);
//	//b = 2;		构造函数没有被explicit修饰时可以如此编写
//	//编译器先将2进行类型转换(用其构造一个匿名对象),再使用构造出的匿名对象完成对b的赋值,之后匿名对象会自动销毁
//	//构造函数被explicit修饰后会被禁止自动的类型转换,加强了代码的可读性
//	b = B(2);
//	return 0;
//}
//
//static成员
//声明为static的类成员称为类的静态成员		修饰成员变量	静态成员变量
//											修饰成员函数	静态成员函数
//静态成员变量一定要在类外进行初始化(被const修饰的除外)
//特性		
//			静态成员为所有类对象所共享的,不属于某个具体的对象,存放在静态区
//			静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
//			类静态成员即可用 类名::静态成员 / 对象.静态成员 来访问
//			静态成员函数没有隐藏的 this指针,不能访问任何非静态成员
//			静态成员也是类的成员,受访问限定符的限制
//			sizeof(类)也不会计算静态成员变量的大小
//static修饰的成员变量相当于在类域中的全局变量
//成员变量的缺省值是给初始化列表使用的,而静态成员变量不进入初始化列表,故静态成员变量不能给默认值
//只有被const修饰的static类型的成员变量可以给默认值,自动定义
//注意:		与static一同使用的const提升了修饰权限,锁住其指向的内存空间,使其不可被修改(使用指针也不行)
//class A
//{
//public:
//	A(int a = 0)
//		: _a(a)
//	{
//		_scount++;
//	}
//	A(const A& aa)
//	//	:_a(aa._a)
//	{
//		_scount++;
//	}
//	static int GetCount();		//没有this指针,只能访问静态成员变量和成员函数
//	//{
//	//	return _scount;
//	//}
//
//private:
//	int _a = 1;
//	static int _scount;		//静态成员变量的声明
//	static const int _st = 10;		//被const修饰的static类型的成员变量可以给默认值,自动定义
//};
//int A::GetCount()
//{
//	return _scount;
//}
//int A::_scount = 0;			//静态成员变量的定义
//void f(A a)
//{
//	;
//}
//int main()
//{
//	static const int st = 10;
//	int* pst = (int*)&st;
//	//(*pst)++;		//报错,访问冲突
//
//	A a1(0);
//	A a2 = a1;
//	f(a2);
//	//静态成员变量的使用,会受访问限定符的限制
//	cout << A::GetCount() << endl;
//	return 0;
//}
//
//友元类
//友元类的所有函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员
//特性:
//			友元的关系是单向的,不具有交换性
//			友元关系是不能传递的
//			友元关系不能继承
//class Time
//{
//public:
//	friend class Date;
//	Time(int hour = 0, int minute = 0, int second = 0)
//		: _hour(hour)
//		, _minute(minute)
//		, _second(second)
//	{}
//private:
//	int _hour;
//	int _minute;
//	int _second;
//};
//class Date
//{
//public:
//	Date(Time& _t, int year = 1900, int month = 1, int day = 1)
//		: _year(year)
//		, _month(month)
//		, _day(day)
//	{
//		_t._hour = 0;
//		_t._minute = 0;
//		_t._second = 0;
//	}
//	void SetTimeOfDate(Time& _t, int hour, int minute, int second)
//	{
//		//直接访问Time类的私有变量
//		_t._hour = hour;
//		_t._minute = minute;
//		_t._second = second;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//
//内部类
//如果将一个类定义在另一个类的内部,就将其叫做内部类
//内部类也是一个独立的类,不属于外部类,不能通过外部类的对象来访问内部类的成员
//外部类对内部类没有任何优越的访问权限
//内部类是外部类的友元类	外部类不是内部类的友元类
//内部类可以通过外部类的对象参数来访问外部类中的所有成员
//特性:
//			内部类可以定义在外部类的任意位置,但受外部类的访问限定符的限制
//			内部类可以直接访问外部类中的static成员,无需外部类的对象/类名
//			sizeof(外部类)=外部类	不计算内部类
//内部类和在全局定义基本是一样的,只是受外部类域名的限制
//class A
//{
//private:
//	static int _s;
//	int _a;
//public:
//	class B
//	{
//	private:
//		int _b;
//	public:
//		void Show(const A& a)
//		{
//			cout << _s << endl;
//			cout << a._a << endl;
//		}
//	};
//};
//int A::_s = 0;
//int main()
//{
//	A();
//	cout << sizeof(A) << endl;
//	cout << sizeof(A::B) << endl;
//	return 0;
//}

//内存管理
//C/C++内存分布
//															32位Linux下所占空间大小
//高地址		内核空间		用户代码不能读写					1G
//				栈				向下增长							8M
//
//				内存映射区		文件映射,动态库,匿名映射
//				堆				向上增长							2G
//
//				数据段			全局数据,静态数据
//低地址		代码段			可执行代码,只读常量
//C语言中动态内存管理方式:
//				malloc		calloc		realloc		free
//C++中仍可以使用C的内存管理方式,但C中的管理方式对于一些场景就会较为麻烦
//
//因此C++提出了自己的内存管理方式	new   delete(关键字操作符) 来进行内存管理
//new申请出的空间无需强制类型转换
//在内置类型上,malloc,free 与 new,delete 无本质区别,只是用法上不同
//C++98中不允许new对数组进行初始化,C++11中允许
//new,delete 与 malloc,free 的本质区别
//new,delete 对于自定义类型成员 开辟/释放 空间的同时,还会调用其的 构造/析构 函数
//而malloc,free仅仅只是开辟与释放空间
//new		先在堆上申请对象空间,再调用构造函数初始化对象
//delete	先调用指针类型析构函数,再释放空间还给堆
//C++提出new和delete,主要是解决两个问题
//						自定义类型对象自动申请的时候,初始化和清理的问题	new/delete会调用构造函数和析构函数
//						new失败了之后要求抛异常,这样才符合面向对象语言的出错处理机制
//new失败之后不会返回NULL,而是抛异常
//在C++中建议使用new,delete进行内存管理
//delete和free一般不会失败,如果失败了,都是释放空间上存在越界或者释放指针位置不对
//
//operator new 与 operator delete函数
//new 和 delete是用户进行动态内存申请和释放的操作符
//operator new 和 operator delete是系统提供的全局函数
//new在底层调用operator new全局函数来申请空间,申请到空间后,再去调用构造函数进行初始化工作
//delete在底层先调用析构函数完成资源清理后,再调用operator delete全局函数释放空间
//operator new:
//该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回
//申请失败后,尝试执行空间不足应对措施,若改应对措施用户设置了,则继续申请,否则就抛异常
//operator delete:
//该函数最终是通过free来释放空间的
//operator new和operator delete不是给用户直接使用的,而是malloc,free的一种封装
//new失败后,不是返回NULL值,而是抛异常
//面向过程的语言,处理错误的方式一般是 返回值+错误码解决(errno) perror库函数会根据errno自动显示错误原因
//										需要用户手动检查
//面向对象的语言,处理错误的方式一般是 抛异常	C++中也要求出错抛异常 try catch
//										异常抛出后必须被捕获,不捕获就会中断程序
//operator new和operator delete对malloc和free封装的意义就在于,失败之后将其改为了抛异常处理
//new和delete的实现原理
//对于内置类型
//				申请内置类型的空间,new,delete与malloc,free基本类似
//		不同处:	new/delete申请和释放单个元素的空间
//				new[]/delete[]申请的是连续的空间
//				new失败后会抛异常,malloc失败后会返回NULL值
//对于自定义类型
//				new:		调用operator new开辟空间,再在申请的空间的执行构造函数
//				delete:		在对象的空间上执行析构函数,调用operator delete释放对象空间
//				new[]:		调用operator new[]函数(该函数实际调用operator new函数)申请空间,在申请的空间上调用N次构造函数
//				delete[]:	在释放的对象空间上调用N次析构函数,再调用operator delete[]函数(实际调用operator delete函数)释放空间
//定位new表达式(placement-new)
//定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
//使用格式	new(place_address)type	/	new(place_address)type(initializer-list)
//			//place-address 要初始化对象的地址
//			//type 对象的类型
//			//initializer-list 构造函数参数列表
//使用场景
//			定位new表达式一般配合内存池使用,因为内存池分配的内存没有初始化
//			若是自定义类型成员,需要使用new的定位表达式进行显示调用构造函数初始化
//同理
//			对象指针->析构函数();
//			free(对象指针);	/	operator delete(对象指针);
//	就可以完成delete的操作了
//使用new定位表达式的对象的空间来源:
//						类* 对象指针 = (类*)malloc(sizeof(类));
//						类* 对象指针 = (类*)operator new(sizeof(类));
//malloc/free和new/delete的区别
//malloc/free和new/delete的共同点是:	都是从堆上申请空间，并且需要用户手动释放
//不同的地方是:
//				malloc和free是函数										new和delete是操作符
//				malloc申请的空间不会初始化								new可以初始化
//				malloc申请空间时,需要手动计算空间大小并传递				new只需在其后跟上空间的类型即可 如果是多个对象,[]中指定对象个数即可
//				malloc的返回值为void*,在使用时必须强转					new不需要,因为new后跟的是空间的类型
//				malloc申请空间失败时,返回的是NULL,因此使用时必须判空	new不需要,但是new需要捕获异常
//				申请自定义类型对象时,malloc / free只会开辟 / 释放空间,不会调用构造函数与析构函数
//				new在申请空间后会调用构造函数完成对象的初始化，delete在释放空间前会调用析构函数完成空间中资源的清理
//
//内存泄漏
//内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况
//并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
//即	动态申请的内存,不是使用了,但有没有主动释放还给操作系统,就存在内存泄露
//内存泄漏的危害
//长期运行的程序出现内存泄漏,影响很大
//出现内存泄漏,会导致响应越来越慢，最终卡死
//		出现泄露的程序进程正常结束,结束时会将内存还给操作系统,危害不大
//		出现泄露的进程非正常结束(如僵尸进程),危害很大,系统会越来越卡,最终卡死
//		需要长期运行的程序出现内存泄露,危害很大(如服务器进程,守护进程)
//C/C++程序中一般我们关心两种方面的内存泄漏：
//堆内存泄漏(Heap leak)
//	堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存
//	用完后必须通过调用相应的 free或者delete 删掉
//	假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
//系统资源泄漏
//	指程序使用系统分配的资源(比方套接字、文件描述符、管道等)没有使用对应的函数释放掉
//	导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定
//检测内存泄露
//在vs下,可以使用windows操作系统提供的 _CrtDumpMemoryLeaks() 函数进行简单检测,该函数只报出了大概泄漏了多少个字节,但没有其他更准确的位置信息
//									将该函数放在main函数之内,每次程序退出的时候检测是否存在内存泄漏,在输出窗口中检测到泄漏的字节数,无具体位置
//int main()
//{
//	int* p = new int[10];
//	_CrtDumpMemoryLeaks();
//	return 0;
//}
//因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放
//但有些情况下总是防不胜防,简单的可以采用上述方式快速定位下
//如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的
//避免内存泄漏
//		工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放
//					理想状态	但碰上异常时,就算注意释放了,还是可能会出问题,需要智能指针来管理才有保证。
//		采用RAII思想或者智能指针来管理资源
// 		有些公司内部规范使用内部实现的私有内存管理库,库自带内存泄漏检测的功能选项
//		出问题了使用内存泄漏工具检测	很多工具都不够靠谱，或者收费昂贵
//总结
//		内存泄漏非常常见,解决方案分为两种:	事前预防型(如智能指针等)	事后查错型(如泄漏检测工具)
//
//class A
//{
//public:
//	A(int a = 0)
//		: _a(a)
//	{
//		cout << "A(int a = 0): " <<  this << endl;
//	}
//	A(int a, int)
//		: _a(a)
//	{
//		cout << "A(int a): " << this << endl;
//	}
//	~A()
//	{
//		cout << "~A(): " << this << endl;
//	}
//private:
//	int _a;
//};
//void NewTest()
//{
//	cout << "void NewTest()" << endl;
//	//动态申请一个元素的空间
//	int* ptr1 = new int;
//	//申请一个元素的空间并初始化
//	int* ptr2 = new int(1);
//	//释放一个元素的空间
//	delete ptr1;
//	delete ptr2;
//
//	//申请一块连续空间
//	int* ptr3 = new int[10];
//	//申请一块连续的空间并初始化,同一般的数组初始化方式相同
//	int* ptr4 = new int[10] { 1, 2, 3, 4, 5 };
//	//释放一块连续的空间
//	delete[] ptr3;
//	delete[] ptr4;
//	cout << "end" << endl;
//}
//void NewClassA()
//{
//	cout << "void NewClassA()" << endl;
//	//对于自定义类型成员,会自动调用其的构造/析构函数
//	A* pa1 = new A;
//	delete pa1;
//	A* pa2 = new A[10];
//	delete[] pa2;
//	cout << endl;
//	//使用new申请对象时调用非默认构造函数
//	A* pa3 = new A(1, 2);
//	delete pa3;
//	A* pa4 = new A[10]{ {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6} };
//	delete[] pa4;
//	cout << "end" << endl;
//}
//void TryCatch()
//{
//	char* p = nullptr;
//	//抛异常,了解一下暂时
//	try
//	{
//		//只会检测try中抛出的异常
//		//可以跨函数,只要有异常,将其抛出即可,自动跳转至捕获
//		p = new char[1024ull * 1024ull * 1024ull * 2ull];
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	printf("%p\n", p);
//}
//int main()
//{
//	NewTest();
//	NewClassA();
//
//	//new定位表达式
//	A* pa1 = (A*)malloc(sizeof(A));		//适用于内存不是直接从堆上申请出的
//	new(pa1)A;
//	pa1->~A();
//	free(pa1);
//
//	A* pa2 = (A*)operator new(sizeof(A));
//	new(pa2)A(2);
//	pa1->~A();
//	operator delete(pa2);
//
//	return 0;
//}

//泛型编程
//有些函数其执行代码通用,仅类型不同
//可以使用函数重载完成:	但重载的函数仅仅是类型不同,代码复用率低,只要有新类型出现,就需要用户手动增加对应的函数
//						代码的可维护性比较低,一个出错可能所有的全出错
//而泛型编程:	就是靠给编译器一个模子,让编译器根据不同类型利用模子生成代码
//				编写与类型无关的通用代码	是代码服用的一种手段,模板是泛型编程的基础
//
//模板		函数模板
//			类模板
//函数模板
//函数模板代表了一个函数家族,该函数模板与类型无关
//在使用时被参数化,根据实参类型产生函数的特定类型版本
//template<typename T1, typename T2, ... , typename TN>			//模板参数(模板类型)
//			typename 后面的类型名字 由用户自由取名(一般为大写字母或单词首字母大写)
//			T1 代表是一个模板类型(虚拟类型)
//template是用来定义模板参数的关键字,也可以用class(不能使用struct替代class)
//template的作用范围仅在最近的一个 {}代码块 中有效
//template只能在全局域和类域中定义
//
//函数模板是一个蓝图,其本身不是函数,是编译器用使用方式产生特定具体类型函数的模具
//		模板就是将本应用户做的重复的事情交给编译器来做
//在编译器编译阶段对于模板函数的使用，编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用
//										确定了实参类型后,会专门生成一份处理实参类型的代码
//函数模板的实例化
//	用不同类型的参数使用函数模板,称为函数模板的实例化
//模板参数实例化分为:		隐式实例化
//							显式实例化
//隐式实例化
//	让编译器根据实参推演模板参数类型(编译器自动推演)
//在模板中,编译器一般不会进行类型转换操作
//当编译器无法通过传入实参的类型来推演模板参数类型时,就会报错
//处理方式:		用户手动强制转化
//				使用显示实例化
//显示实例化
//	在函数名后的< >中指定参数的实际类型
//若传入实参类型与指定类型不匹配,编译器会尝试进行隐式类型转换,若无法转换成功编译器就会报错
//template<typename T1>
//void Swap(T1& left, T1& right)
//{
//	T1 tmp = left;
//	left = right;
//	right = tmp;
//}
//template<typename T>
//T Add(const T& left, const T& right)
//{
//	return left + right;
//}
//template<typename T>
//T* Alloc(size_t n)			//显示实例化常用的场景
//{
//	return new T[n];
//}
//int main()
//{
//	//隐式实例化
//	int a = 0, b = 1;
//	cout << "Before: " << a << ' ' << b << endl;
//	Swap(a, b);					//call        Swap<int> (07FF7A4441456h)
//	cout << "Swap<int>: " << Swap<int> << endl;
//	cout << "After: " << a << ' ' << b << endl;
//	
//	cout << endl;
//
//	double x = 1.1, y = 2.2;
//	cout << "Before: " << x << ' ' << y << endl;
//	Swap(x, y);					//call        Swap<double> (07FF7A444145Bh)
//	cout << "Swap<double>: " << Swap<double> << endl;
//	cout << "After: " << x << ' ' << y << endl;
//
//	//Swap(a, x);		//该语句不能通过编译
//	//在编译期间,当编译器看到该实例化时,需要推演其实参类型
//	//通过实参 a 将 T1 推演为 int, 通过实参 x 将 T1 推演为 double, 但模板参数列表中只有一个T1
//	//编译器因无法确定此处 T1 的类型而报错
//	//注意: 在模板中,编译器一般不会进行类型转换操作
//	cout << endl;
//	
//	//用户手动强制转化
//	cout << "Add: " << Add(a, (int)x) << endl;
//	
//	//使用显式实例化
//	cout << "Before: " << Add<double>(a, x) << endl;
//
//	int* pa = Alloc<int>(10);
//	delete[] pa;
//	
//	return 0;
//}
//模板参数的匹配原则
//一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
//对于非模板函数和同名函数模板
//		如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例
//		如果模板可以产生一个具有更好匹配的函数,那么将选择模板
//模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
//int Add(int left, int right)
//{
//	return left + right;
//}
//template<typename T1, typename T2>
//T1 Add(T1 left, T2 right)
//{
//	return left + right;
//}
//int main()
//{
//	//与非模板函数匹配,编译器不需要特化
//	cout << Add(1, 2) << endl;
//	//调用编译器特化的Add版本
//	cout << Add<int>(1, 2) << endl;
//	//模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
//	cout << Add(1, 2.0) << endl;
//	
//	return 0;
//}
//类模板
//类模板的定义格式
//template<class T1, class T2, ..., class Tn>
//class 类模板名
//{
//	//类内成员定义声明
//};
//类模板的实例化
//		类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,
//类模板名字不是真正的类,而实例化的结果才是真正的类
//对于普通类而言,类名即为类型
//在类模板中,二者不同
//			类名	即为定义/声明类时的类名
//			类型	类名<type>
//
//动态顺序表 Vector演示
//注意:	Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
//template<class T>
//class Vector
//{
//public :
//	Vector(size_t capccity = 10)
//		: _pData(new T[capacity])
//		, _size(0)
//		, _capacity(capacity)
//	{}
//	~Vector();
//
//private :
//	T* _pData;
//	size_t size;
//	size_t capacity;
//};
//template<class T>
//Vector<T>::~Vector()			//类模板中函数放在类外进行定义时,需要加模板参数列表
//{
//	if (_pData != nullptr)
//		delete[] _pData;
//	_size = _capacity = 0;
//}
//#include <Stack>
//int main()
//{
//	// Vector类名	Vector<type>才是类型
//	Vector<int> vt1;
//	Vector<double> vt2;
//	return 0;
//}

//STL
//STL(standard template libaray-标准模板库),是C++标准库的重要组成部分
//不仅是一个可复用的组件库，而且是一个包罗数据结构与算法的软件框架
//STL的版本
//原始版本		Alexander Stepanov、Meng Lee在惠普实验室完成的原始版本
//				本着开源精神他们声明允许任何人任意运用,拷贝,修改,传播,商业使用这些代码,无需付费,唯一的条件就是也需要向原始版本一样做开源使用
//				HP 版本--所有STL实现版本的始祖
//P.J.版本		由P.J.Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改
//				可读性比较低,符号命名比较怪异
//RW版本		由Rouge Wage公司开发,继承自HP版本,被 C++ Builder 采用,不能公开或修改,可读性一般
//SGI版本		由Silicon Graphics Computer Systems, Inc公司开发,继承自HP版本
//				被GCC(Linux)采用,可移植性好,可公开,修改甚至贩卖
//				从命名风格和编程风格上看,阅读性非常高
//学习STL要阅读部分源代码,主要参考的就是SGI版本
//STL的六大组件
//STL:			仿函数:		greater/less...
//				算法:		find/swap/reverse/sort/merge...
//				迭代器:		iterator/const_iterator/reverse_iterator/const_reverse_iterator
//				空间配置器:	allocator
//				容器:		string/vector/list/deque/map/set/multimap/mutilset
//				配接器:		stack/queue/priority_queue
//学习STL的三个境界:	熟用STL									能用
//						了解泛型技术内涵与STL的学理乃至创作		明理
//						扩充STL									扩展
//STL的缺陷
//1. STL库的更新慢	上一版靠谱是C++98,中间的C++03基本一些修订,C++11出来已经相隔了13年,STL才进一步更新
//2. STL至今没有支持线程安全 并发环境下需要用户手动加锁,且锁的粒度是比较大的
//3. STL极度的追求效率,导致内部比较复杂
//4. STL的使用会有代码膨胀的问题,会生成多份代码(当然这是模板语法本身导致的)
//

#include <string>
//string
//C语言中的字符串
//是以'\0'结尾的一些字符的集合,C标准库中提供了一些str系列的库函数
//但是这些库函数与字符串是分离开的,不太符合OOP的思想,且底层空间需要用户自己管理,易出现越界访问邓错误
//在OJ中,有关字符串的题目基本以string类的形式出现
//且在常规工作中,为了简单,方便,快捷,基本都在使用string类
//标准库中的string类
//	1. 字符串是表示字符序列的类
//	2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性
//	3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型)
//	4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数
//	5. 这个类独立于所使用的编码来处理字节:若处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(不是实际编码的字符)来操作
//总结：
//	1. string是表示字符串的字符串类
//	2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
//	3. string在底层实际是:	basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
//	4. 不能操作多字节或者变长字符的序列
//在使用string类时，必须包含#include头文件以及using namespace std;
//
//string简易演示
//class string
//{
//private:
//	char* _str;
//	size_t _size;
//	size_t _capacity;
//};
//
//string类的常用接口
//构造函数
//string()					构造空的string类对象，即空字符串
//string(const char* s)		用C-string来构造string类对象
//string(size_t n, char c)	string类对象中包含n个字符c
//string(const string & s)	拷贝构造函数
//string(const string& str, size_t Pos, size_t len = npos)		以str下标为Pos的元素开始拷贝,拷贝len个字符,或拷贝至end of str
//													npos:	static const size_t npos = -1;		typedef unsigned __int64 size_t;(无符号数)
//							当len未给或len的给值大于str剩余拷贝长度,则拷贝至str末尾
//void String()
//{
//	cout << "String:" << endl;
//	string s1;
//	string s2("Hello World");
//	string s3(10, '#');
//	string s4(s2);
//	string s5(s4, 6);
//	string s6(s4, 6, 3);
//	string s7("Hello World", 5);
//
//	cout << s1 << endl;
//	cout << s2 << endl;
//	cout << s3 << endl;
//	cout << s4 << endl;
//	cout << s5 << endl;
//	cout << s6 << endl;
//	cout << s7 << endl << endl;
//}
////赋值函数
////赋值运算符重载函数
////string& operator=(const string& str);		以str给对象赋值
////string& operator=(const char* s);			以C-string给对象赋值
////string& operator=(char c);				以单个字符给对象赋值
////assign函数
////string& assign(const string& str);					//赋值为string对象str
////string& assign(const string& str, size_t subpos, size_t sublen);		//赋值为string对象str从subpos下标开始sublen长度的字符串
////string& assign(const char* s);						//赋值为char*字符串
////string& assign(const char* s, size_t n);				//赋值为char*字符串的前n个字符串
////string& assign(size_t n, char c);						//赋值为n个字符c
////getline函数
////istream& getline(istream & is, string & str, char delim);			//读取一行字符串,delim手动控制结束符
////istream& getline(istream & is, string & str);
//void Assignment()
//{
//	cout << "Assignment:" << endl;
//	string s1;
//	//cin >> s1;					//string重载了 流插入<<		流提取>>
//	//cout << s1 << endl;
//
//	s1 = "haha";
//	cout << s1 << endl;
//	s1 = 'H';
//	cout << s1 << endl;
//	string s2("Hello World");
//	s1 = s2;
//	cout << s1 << endl;
//	s1 += '\0';
//	s1 += "\0";
//	s1 += "ha!";
//	cout << s1 << endl;				//string重载的<<输出,输出内容以size个数为准
//	cout << s1.c_str() << endl;		//常量字符串的输出以'\0'结尾为准
//
//	s1.assign("assign()");
//	cout << s1 << endl;
//	s1.assign(s2, 6, 5);
//	cout << s1 << endl;
//	s1.assign(10, '#');
//	cout << s1 << endl << endl;
//}
////尾插函数
////append函数的操作同构造函数基本相同
////string& append(const string& str);
////string& append(const string& str, size_t subpos, size_t sublen);			sublen无缺省值
////string& append(const char* s);
////string& append(const char* s, size_t n);
////string& append(size_t, char c);
////push_back函数尾插
////void push_back(char c);
////+=操作符重载尾插(底层调用的就是push_back和append)
////string& operator=(const string& str);
////string& operator=(const char* s);
////string* operator=(char c);
////+操作符重载(非成员函数)
////string operator+(const string& dest, const string& str);		//拷贝返回,深拷贝效率低
////string operator+(const string& str, const char* s);
////string operator+(const char* s, const string& str);
////string operator+(const string& str, char c);
////string operator+ (char c, const string& str);
//void PushBack()
//{
//	cout << "PushBack:" << endl;
//	string s;
//	s.push_back('a');			//尾插一个字符,一次只能插入一个字符
//
//	//append的使用与构造函数的使用大多相似
//	s.append("World");			//尾插一个字符串
//	cout << s << endl;
//	s.append(s);				//尾插一个string
//	cout << s << endl;
//	s.append(50, '#');			//尾插n个字符c
//	cout << s << endl;
//
//	s += ' ';					//+=可以尾插string对象,C_string字符串,单个字符
//	s += "World";
//	cout << s << endl << endl;
//}
////iterator是像指针一样的类型(有可能是指针,有可能不是指针)
////但其用法与指针类似
////string,vector不常用iterator,因为其重载的[]更好用(其物理内存空间连续)
////list,map,set,tree只能使用iterator访问,(其物理内存空间不连续)
////iterator是所有容器所通用的,用法都类似
////范围for循环的底层就是调用迭代器完成
////
////正向迭代器
////类型::iterator 迭代器变量名(一般叫做it/iter) = 对象.begin();
////使用迭代器遍历
////		while (迭代器变量名 != 对象.end())
////			...
////iterator begin();					获取一个字符的迭代器
////const_iterator begin() const;		编译器自动匹配
////iterator end();					获取最后一个字符下一个位置的迭代器
////const_iterator end() const;
////const类型迭代器
////类型::const_iterator 迭代器变量名(一般叫做cit/citer) = 对象.begin();
////
////反向迭代器
////类型::reverse_iterator 迭代器变量名(一般叫做rit/riter) = 对象.rbegin();
////使用迭代器遍历
////		while (迭代器变量名 != 对象.rend())
////			...
////reverse_iterator rbegin();					获取一个字符的迭代器
////const_reverse_iterator rbegin() const;		编译器自动匹配
////reverse_iterator rend();						获取最后一个字符下一个位置的迭代器
////const_reverse_iterator rend() const;
////const类型反向迭代器
////类型::const_reverse_iterator 迭代器变量名(一般叫做crit/criter) = 对象.rbegin();
//#include <list>
//void Iterator()
//{
//	cout << "Iterator:" << endl;
//	string s("Hello World");
//	string::iterator iter = s.begin();		//string迭代器
//	while (iter != s.end())
//	{
//		cout << *iter << ' ';
//		iter++;
//	}
//	cout << endl;
//	for (auto ch : s)			//范围for循环的底层就是调用迭代器
//		cout << ch << ' ';
//	cout << endl;
//
//	string::reverse_iterator rit = s.rbegin();			//string反向迭代器
//	while (rit != s.rend())
//	{
//		cout << *rit << ' ';
//		rit++;
//	}
//	cout << endl;
//
//	//其他
//	list<int> lt(10, 1);
//	list<int>::iterator it = lt.begin();
//	while (it != lt.end())
//	{
//		cout << *it << ' ';
//		it++;
//	}
//	cout << endl;
//	for (auto e : lt)
//		cout << e << ' ';
//	cout << endl << endl;
//}
////设置空间
////size_t size() const;			返回字符串有效字符长度
////size_t length() const;		返回字符串有效字符长度
////size_t capacity() const;		返回空间总大小0
////bool empty() const;			检测字符串释放为空串，是返回true，否则返回false
////void clear()					清空有效字符
////void reserve(size_t n = 0)	为字符串预留空间,不改变有效元素个数
////				当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
////void resize(size_t n)			将有效字符的个数改成n个,使用0填充多余的空间
////void resize(size_t n, char c)	将有效字符的个数改成n个,多出的空间用字符c填充
////				resize在改变元素个数时,若是元素个数增多,可能会改变底层容量的大小,若是元素个数减少,底层空间大小不变
////size_t max_size() const;		获取字符串string最大可存储长度
//void Size()
//{
//	cout << "Size:" << endl;
//	string x(50, '#');
//	cout << x.capacity() << endl;
//	x.reserve(10);			//当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
//	cout << x.capacity() << endl;
//	
//	x.resize(10);			//将有效字符的个数改成n个,使用0填充多余的空间
//	cout << x << endl;
//	cout << x.capacity() << endl;		//若是元素个数减少,底层空间大小不变
//	x.resize(80, '*');		//将有效字符的个数改成n个,多出的空间用字符c填充
//	cout << x << endl;
//	cout << x.capacity() << endl;		//若是元素个数增多,可能会改变底层容量的大小
//	
//	x.clear();
//	cout << "clear:" << x << endl;
//	cout << x.capacity() << endl;
//	
//	cout << x.max_size() << endl << endl;
//}
////find函数,在string对象中向后查找,返回起始下标位置
////size_t find(const string& str, size_t pos = 0) const;			//在string对象中查找string类str,从pos下标位置开始
////size_t find(const char* s, size_t pos = 0) const;				//在string对象中查找char*字符串,从pos下标位置开始
////size_t find(const char* s, size_t pos, size_t n) const;		//在string对象中查找char*字符串的前n个字符,从pos下标位置开始
////size_t find(char c, size_t pos = 0) const;
////c_str函数,返回string类中的字符串的地址
////const char* c_str() const;
////data函数,作用同c_str相同
////const char* data() const;
////rfind函数,在string对象中向前查找,返回起始下标位置
////size_t rfind (const string& str, size_t pos = npos) const;	//操作使用方式同find相同
////size_t rfind(const char* s, size_t pos = npos) const;
////size_t rfind(const char* s, size_t pos, size_t n) const;
////size_t rfind(char c, size_t pos = npos) const;
////find_first_of函数,从前向后查找
////size_t find_first_of(const string& str, size_t pos = 0) const;			//从pos位置开始寻找str中的每个字符,并返回该位置的下标
////size_t find_first_of(const char* s, size_t pos = 0) const;				//使用同上
////size_t find_first_of(const char* s, size_t pos, size_t n) const;			//从pos位置开始寻找s字符串中的前n个字符,并返回该位置下标
////size_t find_first_of(char c, size_t pos = 0) const;						//使用同find相同
////find_last_of函数,从后向前查找
////size_t find_last_of(const string& str, size_t pos = npos) const;			//使用方式同find_last_of相同
////size_t find_last_of(const char* s, size_t pos = npos) const;
////size_t find_last_of(const char* s, size_t pos, size_t n) const;
////size_t find_last_of(char c, size_t pos = npos) const;
////find_first_not_of函数,从前向后查找
////size_t find_first_not_of (const string& str, size_t pos = 0) const;		//使用方式同上,功能为返回没有在str中的字符的下标,与find_first_of相反
////size_t find_first_not_of(const char* s, size_t pos = 0) const;
////size_t find_first_not_of(const char* s, size_t pos, size_t n) const;
////size_t find_first_not_of(char c, size_t pos = 0) const;
////find_last_not_of函数,从后向前查找
////size_t find_last_not_of (const string& str, size_t pos = npos) const;		//使用方式同上
////size_t find_last_not_of(const char* s, size_t pos = npos) const;
////size_t find_last_not_of(const char* s, size_t pos, size_t n) const;
////size_t find_last_not_of(char c, size_t pos = npos) const;
//void Find()
//{
//	cout << "Find:" << endl;
//	string s("hello wwororld##hello linux");
//	string f("hello");
//	
//	//size_t find(const string& str, size_t pos = 0) const;
//	cout << "s.find(f): " << s.find(f) << ' ' << s.c_str() + s.find(f) << endl;
//	cout << "s.find(f, 2): " << s.find(f, 2) << ' ' << s.c_str() + s.find(f, 2) << endl;
//
//	//size_t find(const char* s, size_t pos, size_t n) const;
//	cout << "s.find(""world"", 0, 5): " << s.find("world", 0, 3) << ' ' << s.c_str() + s.find("world", 0, 3) << endl;
//
//	//size_t find(char c, size_t pos = 0) const;
//	cout << "s.find('o'): " << s.find('o') << ' ' << s[s.find('o')] << endl;
//	cout << "s.find('o', 10): " << s.find('o', 10) << ' ' << s[s.find('o', 10)] << endl;
//
//	string str("Please, replace the vowels in this sentence by asterisks.");
//	size_t found = str.find_first_of("aeiou");
//	while (found != string::npos)
//	{
//		str[found] = '*';
//		found = str.find_first_of("aeiou", found + 1, 2);
//	}
//	std::cout << str << endl;
//
//	str = "Please, replace the vowels in this sentence by asterisks.";
//	found = str.find_first_not_of("aeiou");
//	while (found != string::npos)
//	{
//		str[found] = '*';
//		found = str.find_first_not_of("aeiou", found + 1, 2);
//	}
//	std::cout << str << endl << endl;
//}
////erase函数
////string& erase(size_t pos = 0, size_t len = npos);			//清除从pos下标开始,向后len个字符,并使后续字符向前覆盖
////iterator erase(iterator p);								//清除该迭代器指向位置的字符
////iterator erase(iterator first, iterator last);			//清除迭代器范围内的字符
////insert函数
////string& insert(size_t pos, const string& str);				//在string对象的pos下标位置插入str字符串
////string& insert(size_t pos, const string& str, size_t subpos, size_t sublen);			//在string对象中插入从str字符串的subpos下标到sublen长度的一段字符串
////string& insert(size_t pos, const char* s);					//在string对象中pos下标位置插入char*字符串
////string& insert(size_t pos, const char* s, size_t n);			//在string对象中插入从char*字符串的sublen长度的一段字符
////string& insert(size_t pos, size_t n, char c);					//在string对象中pos下标位置插入n个字符c	
////void insert(iterator p, size_t n, char c);					//在迭代器p指向位置处插入n个字符c
////replace函数
////string& replace(size_t pos, size_t len, const string& str);				//在pos位置将str全部字符串替换len长度的字符
////string& replace (iterator i1, iterator i2, const string& str);			//将迭代器i1至i2之间的字符串替换为str
////string& replace(size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);			//在pos位置将str字符串从subpos位置开始的sublen长度的字符串替换len长度的字符
////string& replace(size_t pos, size_t len, const char* s);					//使用方式同上
////string& replace(iterator i1, iterator i2, const char* s);
////string& replace(size_t pos, size_t len, const char* s, size_t n);
////string& replace(iterator i1, iterator i2, const char* s, size_t n);
////string& replace(size_t pos, size_t len, size_t n, char c);				//在pos位置将n个字符c替换len长度的字符串
////string& replace(iterator i1, iterator i2, size_t n, char c);
//void Modify()
//{
//	cout << "Modify" << endl;
//	string x = "123456789";
//	x.erase(2, 5);						//清除从pos下标开始,向后len个字符,并使后续字符向前覆盖
//	cout << "erase(2, 5):" << x << endl;
//	x.erase(x.begin() + 1);				//清除该迭代器指向位置的字符
//	cout << "erase(x.begin() + 1):" << x << endl;
//	x.erase(x.begin() + 1, x.end() - 1);//清除迭代器范围内的字符
//	cout << "erase(x.begin() + 1, x.end() - 1):" << x << endl;
//	cout << x.capacity() << endl;		//删除元素不会减少空间
//
//	x.insert(0, "hello");
//	cout << "insert(0, ""hello""):" << x << endl;
//	x.insert(5, "hello linux goodbye", 6, 5);
//	cout << "insert(5, ""hello linux goodbye"", 6, 5):" << x << endl;
//	x.insert(5, 3, ' ');
//	cout << "insert(5, 3, ' '):" << x << endl;
//	x.insert(x.begin(), 1, ' ');
//	cout << "insert(x.begin(), 1, ' '):" << x << endl;
//	cout << x.capacity() << endl;		//插入字符串空间不够时会进行扩容
//
//	x = "Hello World, Linux!";
//	x.replace(6, 0, "Linux ");
//	cout << x << endl;
//	x.replace(x.begin(), x.begin() + 1, "haha");
//	cout << x << endl;
//	x.replace(0, 4, "haHa", 2, 1);
//	cout << x << endl;
//	x.replace(6, 6, 3, ' ');
//	cout << x << endl << endl;
//}
////compare函数
////int compare(const string& str) const;									//string对象与传入str比较字符串内容是否相等
////int compare(size_t pos, size_t len, const string& str) const;						//string对象从pos下标开始的len长度的字符串与传入str内容是否相等
////int compare(size_t pos, size_t len, const string& str, size_t subpos, size_t sublen) const;			//string对象从pos下标开始的len长度的字符串与传入str从subpos下标开始的sublen长度的字符串是否相等
////int compare(const char* s) const;										//使用方式同上
////int compare(size_t pos, size_t len, const char* s) const;
////int compare(size_t pos, size_t len, const char* s, size_t n) const;				//string对象从下标开始的len长度的字符串与char*字符串n长度的内容是否相等
////比较运算符重载
////bool operator==(const string& lhs, const string& rhs); bool operator==(const char* lhs, const string& rhs); bool operator==(const string& lhs, const char* rhs);
////bool operator!=(const string& lhs, const string& rhs); bool operator!=(const char* lhs, const string& rhs); bool operator!=(const string& lhs, const char* rhs);
////bool operator<(const string& lhs, const string& rhs); bool operator<(const char* lhs, const string& rhs); bool operator<(const string& lhs, const char* rhs);
////bool operator<=(const string& lhs, const string& rhs); bool operator<=(const char* lhs, const string& rhs); bool operator<=(const string& lhs, const char* rhs);
////bool operator>(const string& lhs, const string& rhs); bool operator>(const char* lhs, const string& rhs); bool operator>(const string& lhs, const char* rhs);
////bool operator>=(const string& lhs, const string& rhs); bool operator>=(const char* lhs, const string& rhs); bool operator>=(const string& lhs, const char* rhs);
//void Compare()
//{
//	cout << "Compare:" << endl;
//	string s("ABCDEFG12345");
//	cout << s.compare("ABCDEFG12345") << endl;
//	cout << s.compare(0, 7, "ABCDEFG") << endl;
//	cout << s.compare(7, 5, "abcdefg12345", 7, 8) << endl;
//	cout << (s == "ABCDEFG12345") << endl;
//	cout << (s == "abcdef") << endl;
//	cout << (s >= "12345") << endl;
//	cout << (s <= "12345") << endl << endl;
//}
////substr函数
////string substr(size_t pos = 0, size_t len = npos) const;
//void Substr()
//{
//	string url = "https://legacy.cplusplus.com/reference/string/string/find/";
//
//	string protocol;
//	size_t pos1 = url.find("://");
//	if (pos1 != string::npos)
//		protocol = url.substr(0, pos1);
//	cout << protocol << endl;
//
//	string domain;
//	string uri;
//	size_t pos2 = url.find('/', pos1 + string("://").size());
//	if (pos2 != string::npos)
//	{
//		domain = url.substr(pos1 + string("://").size(), pos2 - (pos1 + string("://").size()));
//		uri = url.substr(pos2 + 1);
//	}
//	cout << domain << endl;
//	cout << uri << endl;
//
//	string _uri;
//	size_t pos3 = url.find('/', pos2 + 1);
//	while (pos3 != string::npos)
//	{
//		_uri = url.substr(pos2 + 1, pos3 - (pos2 + 1));
//		pos2 = pos3;
//		pos3 = url.find('/', pos2 + 1);
//		cout << _uri << endl;
//	}
//	cout << endl;
//}
////数字与字符串互转
////number->string
////string to_string(int val);
////string to_string(long val);
////string to_string(long long val);
////string to_string(unsigned val);
////string to_string(unsigned long val);
////string to_string(unsigned long long val);
////string to_string(float val);
////string to_string(double val);
////string to_string(long double val);
////string->number
////double stod(const string&  str, size_t* idx = 0);			//idx为标识成功转换的字符串长度
////float stof(const string& str, size_t* idx = 0);
////long double stold(const string& str, size_t* idx = 0);
////int stoi(const string& str, size_t* idx = 0, int base = 10);			//base标识字符串的数字进制,在将其转化为数字时自动转为十进制
////long stol(const string& str, size_t* idx = 0, int base = 10);
////long long stoll(const string& str, size_t* idx = 0, int base = 10);
////unsigned long stoul(const string& str, size_t* idx = 0, int base = 10);
////unsigned long long stoull(const string& str, size_t* idx = 0, int base = 10);
//void Number_String()
//{
//	cout << to_string(1) << endl;
//	cout << to_string(1 + 2 + 3 + 4 + 5) + " Hello Good" << endl;
//	cout << to_string(3.14) << endl;
//
//	size_t pos = -1;
//	double d = stod("3.14@15926", &pos);
//	cout << pos << ' ' << d << endl;
//	int i = stoi("10", &pos, 10);
//	cout << pos << ' ' << i << endl;
//	i = stoi("11", &pos, 10);
//	cout << pos << ' ' << i << endl;
//	i = stoi("AA", &pos, 16);
//	cout << pos << ' ' << i << endl;
//	i = stoi("11111111", &pos, 2);
//	cout << pos << ' ' << i << endl;
//}
//int main()
//{
//	String();			//构造
//
//	Assignment();		//赋值+输入输出
//
//	PushBack();			//尾插
//
//	Iterator();			//迭代器
//
//	Size();				//空间
//
//	Find();				//查询
//
//	Modify();			//修改插入删除
//
//	Compare();			//字符串比较
//
//	Substr();			//提取字符串
//
//	Number_String();		//字符串与数字互转
//
//	return 0;
//}
//
//string模拟
//浅拷贝
//也称位拷贝	编译器只是将对象中的值拷贝过来	(对于类来说:编译器自动生成的拷贝构造函数为浅拷贝)
//如果对象中存在管理资源,就会导致多个对象共享同一份资源
//当一个对象销毁时就会销毁该资源,而其他对象不知道该资源已被释放,当继续对资源进项操作时,就会发生了越界访问(未定义行为)
//type* pa = new type[n];
//type* pb = pa;
//可以采用深拷贝解决浅拷贝的管理资源问题	即:每个对象都有一份独立的资源,不和其他对象共享
//深拷贝
//如果一个类中涉及到资源的管理,其拷贝构造函数 赋值运算符重载以及析构函数必须要显式给出
//一般情况都是按照深拷贝方式提供
//type* pa = new type[n];
//type* pb = new type[n];
//memcpy(pb, pa, sizeof(type) * n);
//写时拷贝
//写时拷贝就是在浅拷贝的基础之上增加了引用计数的方式来实现
//当拷贝后未对其内容进行写入,仅仅是读取等操作时,拷贝目标与拷贝对象使用同一块资源
//			但对拷贝对象进行写入等更改内容的操作时,才进行深拷贝
//以达到减少计算机资源使用的目的,一定程度上提高效率
//引用计数:	用来记录资源使用者的个数
//在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1
//			当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源
//如果计数为1,说明该对象时资源的最后一个使用者,需要将该资源释放,否则就不释放
//class A			//仅为示例演示引用计数
//{
//public:
//	A(int n = 10)
//	{
//		arr = new char[n];
//		_count++;
//	}
//	~A()
//	{
//		if (_count == 1)
//			delete[] arr;
//		_count--;
//	}
//private:
//	static int _count;
//	char* arr;
//};
//int A::_count = 0;
//
//测试实现string
#include "string.h"
namespace qyc
{
	namespace test
	{
		void String_String()
		{
			string s1("Hello World");
			string s2;
			string s3(s1);
			string s4(s1, 6, 3);
			cout << s1.c_str() << endl;
			cout << s2.c_str() << endl;
			cout << s3.c_str() << endl;
			cout << s4 << endl;

			s2 = s3;
			cout << s2.c_str() << endl;
			s2 = s2;
			cout << s2.c_str() << endl;

			for (size_t i = 0; i < s1.size(); ++i)
				cout << s1[i] << ' ';
			cout << endl;
			for (size_t i = 0; i < s1.size(); ++i)
				cout << ++s1[i] << ' ';
			cout << endl;
			cout << s1 << endl;
			//cin >> s1;
			//cout << s1 << endl << endl;
			s1 = "hello world";
			s1.resize(3);
			cout << s1 << endl;
			s1 = "hello world";
			s1.resize(20, 'x');
			cout << s1 << endl << endl;
		}

		void String_Iterator()
		{
			string s1("Hello World");
			string::iterator it = s1.begin();
			while (it != s1.end())
				cout << *it++ << ' ';
			cout << endl;
			for (auto& e : s1)
				cout << ++e << ' ';
			cout << endl << endl;
		}

		void String_Push()
		{
			string s1("Hello World");
			s1.push_back('!');
			s1 += ' ';
			s1 += 'x';
			s1 += "HaHa ";
			cout << s1.c_str() << endl;

			string s("hello world");
			s += '\0';
			s += "hello";
			cout << s << endl;
			string s3("hehe ");
			s3 += s;
			cout << s3 << endl;

			string s2;
			s2 += "Haha";
			cout << s2.c_str() << endl;
			s2 += s2;
			cout << s2 << endl << endl;
		}

		void String_Insert_Erase()
		{
			string s1;
			s1.insert(0, '1');
			cout << s1 << endl;
			s1 = "Hello World!!! Hello Linux!!!";
			s1.insert(5, '1');
			cout << s1 << endl;

			s1.insert(5, "Haha");
			cout << s1 << endl;

			s1 = "Hello World!!!";
			s1.insert(0, s1);
			cout << s1 << endl;
			s1.insert(14, ' ');
			cout << s1 << endl;
			s1.insert(6, "");
			cout << s1 << endl;

			s1.erase(5, 5);
			cout << s1 << endl;
			s1.erase(5, 1);
			cout << s1 << endl;
			s1.erase(7);
			cout << s1 << endl << endl;
		}

		void String_Substr()
		{
			string url = "https://legacy.cplusplus.com/reference/string/string/find/";
			cout << url << endl;
			string protocol;
			size_t pos1 = url.find("://");
			if (pos1 != string::npos)
				protocol = url.substr(0, pos1);
			cout << protocol << endl;

			string domain;
			string uri;
			size_t pos2 = url.find('/', pos1 + string("://").size());
			if (pos2 != string::npos)
			{
				domain = url.substr(pos1 + string("://").size(), pos2 - (pos1 + string("://").size()));
				uri = url.substr(pos2 + 1);
			}
			cout << domain << endl;
			cout << uri << endl;

			string _uri;
			size_t pos3 = url.find('/', pos2 + 1);
			while (pos3 != string::npos)
			{
				_uri = url.substr(pos2 + 1, pos3 - (pos2 + 1));
				pos2 = pos3;
				pos3 = url.find('/', pos2 + 1);
				cout << _uri << endl;
			}
			cout << endl;
		}

		void String_Compare()
		{
			cout << "String_Compare" << endl;
			string s1("hello");
			string s2("hello");
			cout << (s1 == s2) << ' ';
			cout << (s1 > s2) << ' ';
			cout << (s1 < s2) << endl;

			s2 = "helloxxx";
			cout << (s1 == s2) << ' ';
			cout << (s1 > s2) << ' ';
			cout << (s1 < s2) << endl;

			s1 = "hello\0\0\0\0xxxxx";
			cout << (s1 == s2) << ' ';
			cout << (s1 > s2) << ' ';
			cout << (s1 < s2) << endl;
			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		qyc::test::String_String();
//		qyc::test::String_Iterator();
//		qyc::test::String_Push();
//		qyc::test::String_Insert_Erase();
//		qyc::test::String_Substr();
//		qyc::test::String_Compare();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//
//	return 0;
//}
//
//string练习题
//反转字母
//bool IsLetter(const char c)
//{
//	if (c >= 'a' && c <= 'z')
//		return true;
//	if (c >= 'A' && c <= 'Z')
//		return true;
//	return false;
//}
//string reverseOnlyLetters(string s)
//{
//	for (int begin = 0, end = s.size() - 1; begin < end; begin++, end--)
//	{
//		while (end > begin && !IsLetter(s[end]))
//			end--;
//		while (begin < end && !IsLetter(s[begin]))
//			begin++;
//		swap(s[begin], s[end]);
//	}
//	return s;
//}
////寻找只出现一次的数字(返回第一个)
//int firstUniqChar(string s)
//{
//	int map[260] = { 0 };
//	for (auto e : s)
//		map[e]++;
//	int index = -1;
//	for (int i = 0; i < s.size() && index == -1; i++)
//	{
//		if (map[s[i]] == 1)
//			index = i;
//	}
//	return index;
//}
////字符串最后一个单词长度
//int LastWordLength(string& str)
//{
//	/*string::reverse_iterator rit = str.rbegin();
//	int count = 0;
//	while (rit != str.rend())
//	{
//		if (*rit == ' ')
//			break;
//		count++;
//		rit++;
//	}*/
//	int count = 0;
//	for (string::reverse_iterator rit = str.rbegin(); rit != str.rend() && *rit != ' '; rit++, count++);
//	//for (auto rit = str.rbegin(); rit != str.rend() && *rit != ' '; rit++, count++);
//	return count;
//}
////int main()
////{
////	string str;
////	getline(cin, str);
////	cout << LastWordLength(str) << endl;
////}
////验证一个字符串是否是回文
//char CharOperation(char c)
//{
//	if (c >= 'A' && c <= 'Z')
//		return c + 32;
//	else
//		return c;
//}
//bool isPalindrome(string s)
//{
//	char c = 0;
//	for (int begin = 0, end = s.size() - 1; begin < end; begin++, end--)
//	{
//		while (begin < end && !isalnum(s[begin]))
//			begin++;
//		while (begin < end && !isalnum(s[end]))
//			end--;
//		if (CharOperation(s[begin]) != CharOperation(s[end]))
//			return false;
//	}
//	return true;
//}
////字符串数字相加
//void reverse(string& str)
//{
//	int begin = 0, end = str.size() - 1;
//	while (begin < end)
//	{
//		swap(str[begin], str[end]);
//		begin++, end--;
//	}
//}
//string addStrings(string num1, string num2)
//{
//	string sum;
//	int end1 = num1.size() - 1, end2 = num2.size() - 1;
//	while (end1 >= 0 || end2 >= 0)
//	{
//		int tmp1 = end1 >= 0 ? num1[end1] - '0' : 0;
//		int tmp2 = end2 >= 0 ? num2[end2] - '0' : 0;
//		tmp1 += tmp2;
//		sum += tmp1 + '0' + sum[sum.size()];
//		if (sum[sum.size() - 1] > '9')
//		{
//			sum[sum.size() - 1] -= 10;
//			sum[sum.size()] += 1;
//		}
//		end1--, end2--;
//	}
//	if (sum[sum.size()] != '\0')
//		sum += '0' + sum[sum.size()];
//	reverse(sum);
//	return sum;
//}
////翻转字符串(区间部分翻转)
//void reverse(string& s, int begin, int end)
//{
//	end--;
//	while (begin < end)
//	{
//		swap(s[begin], s[end]);
//		begin++, end--;
//	}
//}
//string reversestr(string s, int k)
//{
//	bool flag = true;
//	for (int begin = 0, end = begin + k; begin < s.size(); begin = end, end += k, flag = !flag)
//	{
//		if (end > s.size())
//			end = s.size();
//		if (flag)
//			reverse(s, begin, end);
//	}
//	return s;
//}
////翻转字符串(翻转字符串中的单词)
//string reverseWords(string s)
//{
//	int prev = 0, cur = 0;
//	while (cur < s.size())
//	{
//		while (cur < s.size() && s[cur] != ' ')
//			cur++;
//		reverse(s, prev, cur);
//		prev = ++cur;
//	}
//	return s;
//}
////字符串相乘
//string multiply(string num1, string num2)
//{
//	if (num1 == "0" || num2 == "0")
//		return "0";
//	string mul;
//	int end1 = num1.size() - 1;
//	while (end1 >= 0)
//	{
//		int end2 = num2.size() - 1;
//		string s(num1.size() - end1 - 1, '0');
//		while (end2 >= 0)
//		{
//			int tmp1 = num1[end1] - '0', tmp2 = num2[end2] - '0';
//			tmp1 *= tmp2;
//			s += tmp1 + s[s.size()];
//			if (s[s.size() - 1] > 9)
//			{
//				s[s.size()] = s[s.size() - 1] / 10;
//				s[s.size() - 1] %= 10;
//			}
//			s[s.size() - 1] += '0';
//			end2--;
//		}
//		if (s[s.size()] != '\0')
//			s += '0' + s[s.size()];
//		reverse(s);
//		mul = addStrings(mul, s);
//		end1--;
//	}
//	return mul;
//}
////字符串转int
//int StrToInt(string str)
//{
//	char flag = 1;
//	int begin = 0;
//	if (str[begin] == '-' && ++begin)
//		flag = -1;
//	else if (str[begin] == '+' && ++begin)
//		flag = 1;
//	long long int num = 0;
//	while (begin < str.size())
//	{
//		num *= 10;
//		if (!isdigit(str[begin]))
//			return 0;
//		num += str[begin++] - '0';
//		if (num > INT_MAX)
//			return 0;
//	}
//	return (int)num * flag;
//}
////逆序字符串
//#include <vector>
//template<class T>
//void Reverse(T begin, T end)
//{
//	while (begin != end && begin != --end)
//		swap(*begin++, *end);
//}

#include <vector>
//vector
//vector是表示可变大小数组的序列容器
//	就像数组一样,vector也采用的连续存储空间来存储元素,可以采用下标对vector的元素进行访问和数组一样高效
//					它的大小是可以动态改变的,而且它的大小会被容器自动处理
//vector使用动态分配数组来存储它的元素
//当新元素插入时候,这个数组需要被重新分配大小增加存储空间
//		分配一个新的数组,然后将全部元素移到这个数组,时间复杂度O(N)
//因此vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间更大
//		不同的库采用不同的策略权衡空间的使用和重新分配
//vector占用了更多的存储空间,获得了管理存储空间的能力,并且以一种有效的方式动态增长
//与其它动态序列容器相比(deque, list and forward_list)
//		vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效
//		对于其它不在末尾的删除和插入操作,效率更低
//		比起list和forward_list统一的迭代器和引用更好
//vector的定义
//std::vector
//template <class T, class Alloc = allocator<T>> class vector;
//vector类函数操作与string操作类似,不再一一列写
//除string类中单独提供了find函数,其他容器均未单独提供find函数
//应使用算法模块中实现的find函数
//迭代器失效
//迭代器的主要作用就是让算法不用关心底层的数据结构
//					其底层实际就是一个指针,或者对指针进行了封装
//迭代器失效:实际就是迭代器底层对应指针所指向的空间被销毁,变成了野指针
//						若继续使用失效的迭代器,程序肯能会崩溃
//引起迭代器失效的可能方式
//			1.	调用算法可能会引起其底层空间改变的
//				如: resize	reserve	insert	assign	push_back
// 
//			2.	指定位置删除的操作	(当pos为最后一个位置时,erase会导致pos失效)
//				如: erase
//SGI STL中,迭代器失效后,代码并不一定崩溃,但其运行结果一定不对
//						若 it 不在 begin 至 end 之间,一定会崩溃
//迭代器失效的解决方案:			在使用前,对迭代器重新赋值
//
#include <algorithm>		//算法模块
//void Vector_Vector()
//{
//	cout << "Vector_Vector" << endl;
//	vector<int> v1;
//	vector<int> v2(10, 5);
//	vector<int> v3(v2);
//	cout << endl;
//}
//void Vector_Push()
//{
//	cout << "Vector_Push()" << endl;
//	vector<int> v1(1, 1);
//	v1.push_back(2);
//	v1.push_back(3);
//	v1.push_back(4);
//	v1.push_back(5);
//
//	for (size_t i = 0; i < v1.size(); ++i)
//		cout << v1[i]++ << ' ';
//	cout << endl;
//	vector<int>::iterator it = v1.begin();
//	while (it != v1.end())
//		cout << ++(*it++) << ' ';
//	cout << endl;
//	for (auto& e : v1)
//		cout << --e << ' ';
//	cout << endl << endl;
//}
//void Vector_Find()
//{
//	cout << "Vector_Find" << endl;
//	vector<int> v1;
//	for (int i = 0; i < 10; i++)
//		v1.push_back(i);
//	//find算法函数查找区间[first， last),找不到返回last
//	vector<int>::iterator pos = find(v1.begin(), v1.end(), 5);
//	if (pos != v1.end())
//		v1.insert(pos, 50);
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl;
//	pos = find(v1.begin(), v1.end(), 50);
//	if (pos != v1.end())
//		v1.erase(pos);
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl << endl;
//}
//void Vector_Compare()
//{
//	cout << "Vector_Compare" << endl;
//	vector<int> v1(10, 1);
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl;
//	vector<int> v2;
//	for (int i = 0; i < 10; ++i)
//		v2.push_back(i);
//	for (auto e : v2)
//		cout << e << ' ';
//	cout << endl;
//	//vector重载了比较运算符,但仅支持相同类型进行比较
//	if (v1 > v2)
//		cout << "v1 > v2" << endl;
//	else if (v1 == v2)
//		cout << "v1 == v2" << endl;
//	else
//		cout << "v1 < v2" << endl;
//	cout << endl;
//}
#include <functional>
//void Vector_Sort()
//{
//	cout << "Vector_Sort" << endl;
//	
//	vector<int> v1;
//	v1.push_back(3);
//	v1.push_back(5);
//	v1.push_back(7);
//	v1.push_back(1);
//	v1.push_back(2);
//	v1.push_back(9);
//	v1.push_back(4);
//	v1.push_back(6);
//	v1.push_back(0);
//	v1.push_back(8);
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl;
//
//	//sort函数区间同find相同,默认为升序
//	sort(v1.begin(), v1.end());
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl;
//
//	less<int> ls;		//算法库中默认包括了less,但没有包括greater
//	greater<int> gt;	//在functional库中
//	//sort(v1.begin(), v1.end(), gt);
//	sort(v1.begin(), v1.end(), greater<int>());
//	for (auto e : v1)
//		cout << e << ' ';
//	cout << endl;
//	cout << endl;
//}
//int main()
//{
//	Vector_Vector();
//	Vector_Push();
//	Vector_Find();
//	Vector_Compare();
//	Vector_Sort();
//
//	Solution().letterCombinations("23");
//
//	return 0;
//}
//测试实现vector
#include "vector.h"
namespace qyc
{
	namespace test
	{
		void Vector_Vector()
		{
			cout << "Vector_Vector" << endl;

			vector<int> v1;
			for (int i = 0; i < 10; i++)
				v1.push_back(i);
			for (const auto& e : v1)
				cout << e << ' ';
			cout << endl;

			vector<int> v2 = v1;
			for (const auto& e : v2)
				cout << e << ' ';
			cout << endl;

			string s("abcdefghjkl");
			vector<int> v3(s.begin(), s.end());
			for (const auto& e : v3)
				cout << e << ' ';
			cout << endl;

			v3 = v1;
			for (const auto& e : v3)
				cout << e << ' ';
			cout << endl;

			vector<int> v4(10);
			for (const auto& e : v4)
				cout << e << ' ';
			cout << endl;

			cout << v1.front() << ' ' << v1.back() << endl;
			for (int i = 5; i > 0; --i)
			{
				cout << v2.back() << endl;
				v2.pop_back();
			}

			vector<string> vs;
			vs.push_back("hello");
			vs.push_back("Hello");
			vs.push_back("HELLO");
			vs.push_back("world");
			vs.push_back("World");
			vs.push_back("WORLD");
			for (const auto& e : vs)
				cout << e << endl;

			cout << endl;
		}

		void Vector_Modify()
		{
			cout << "Vector_Modify" << endl;

			vector<int> v1;
			for (int i = 0; i < 10; i++)
				v1.push_back(i);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			v1.insert(v1.begin() + 5, 50);
			v1.insert(v1.end() - 1, 10);
			v1.insert(v1.begin(), -1);
			v1.insert(v1.end() - 2, 90);
			v1.insert(v1.end() - 3, 80);
			v1.insert(v1.begin() + 6, 60);
			v1.insert(v1.begin() + 4, 40);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			v1.erase(v1.begin() + 4);
			v1.erase(v1.begin() + 6);
			v1.erase(v1.end() - 4);
			v1.erase(v1.end() - 3);
			v1.erase(v1.begin());
			v1.erase(v1.end() - 2);
			v1.erase(v1.begin() + 5);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			vector<int> tmp = v1;
			v1.erase(v1.begin(), v1.begin() + 5);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;
			v1 = tmp;
			v1.erase(v1.end() - 5, v1.end());
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;
			cout << v1.capacity() << endl;
			v1.insert(v1.begin(), 30, 0);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;
			cout << v1.size() << ' ' << v1.capacity() << endl;

			v1 = tmp;
			v1.erase(v1.begin(), v1.end());
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			string x("hello world");
			v1.insert(v1.begin(), x.begin(), x.end());
			for (auto e : v1)
				printf("%c", e);
			cout << endl;

			cout << endl;
		}

		void Vector_Iterator()
		{
			cout << "Vector_Iterator" << endl;
			vector<int> v1;
			v1.reserve(10);
			cout << *v1.begin() << endl;
			cout << v1.capacity() << endl;
			for (int i = 1; i <= 10; i++)
				v1.push_back(i);
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			for (vector<int>::iterator it = v1.begin(); it != v1.end(); ++it)
			{
				if (*it % 2 == 0)
					it = v1.insert(it, *it * 2) + 1;
				cout << *it << endl;
				cout << v1.capacity() << endl;
			}
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			for (vector<int>::iterator it = v1.begin(); it != v1.end(); ++it)
			{
				if (*it % 2 == 0)
					it = v1.erase(it) - 1;
			}
			for (auto e : v1)
				cout << e << ' ';
			cout << endl;

			vector<int>::reverse_iterator i = v1.rbegin();
			while (i != v1.rend())
			{
				cout << *i << ' ';
				++i;
			}
			cout << endl;
			cout << endl;
		}

		void Vector_Resize()
		{
			cout << "Vector_Resize" << endl;
			vector<int> v5;
			v5.resize(10, 5);
			for (auto e : v5)
				cout << e << ' ';
			cout << endl;

			for (int i = 1; i < 20; i++)
				v5.push_back(i);
			for (auto e : v5)
				cout << e << ' ';
			cout << endl;

			v5.resize(35, 2);
			for (auto e : v5)
				cout << e << ' ';
			cout << endl;

			v5.resize(3, 2);
			for (auto e : v5)
				cout << e << ' ';
			cout << endl << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		qyc::test::Vector_Vector();
//		qyc::test::Vector_Modify();
//		qyc::test::Vector_Iterator();
//		qyc::test::Vector_Resize();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	return 0;
//}
//
//vector练习题
////返回只出现一次的一个数(其余数字均出现两次)
//int singleNumber_1(vector<int>& nums)
//{
//	int single = 0;
//	for (auto e : nums)
//		single ^= e;		//按位异或即可
//	return single;
//}
////找到数组中只出现一次的一个数(其余数字均出现三次)
//int singleNumber_2(vector<int>& nums)
//{
//	int ans = 0;
//	//单独计算所有数字的所有二进制位,出现三次的数字二进制位之后必为0或3,因此每位和结果%3,再加回ans即可
//	for (size_t i = 0; i < 32; i++)
//	{
//		unsigned int tmp = 0;
//		for (unsigned int e : nums)
//			tmp += ((e >> i) & 1);
//		ans += (tmp % 3) << i;
//	}
//	return ans;
//}
////找到数组中只出现一次的两个数(其余数字均出现两次)
//vector<int> singleNumber_3(vector<int>& nums)
//{
//	int cmp = 0;
//	for (auto e : nums)
//		cmp ^= e;
//	size_t flag = 0;
//	while (flag < 32)
//	{
//		if ((cmp >> flag) & 1)
//			break;
//		flag++;
//	}
//	vector<int> ret(2, 0);
//	for (auto e : nums)
//	{
//		if ((e >> flag) & 1)
//			ret[0] ^= e;
//		else
//			ret[1] ^= e;
//	}
//	return ret;
//}
////返回杨辉三角的前numRows行
//vector<vector<int>> generate(int numRows)
//{
//	vector<vector<int>> T;
//	for (size_t i = 0; i < numRows; ++i)
//	{
//		T.push_back(vector<int>(i + 1, 0));
//		for (size_t j = 0; j <= i; ++j)
//		{
//			if (0 == j || i == j)
//				T[i][j] = 1;
//			else
//				T[i][j] = T[i - 1][j] + T[i - 1][j - 1];
//		}
//	}
//	return T;
//}
////有序数组去重
//size_t removeDuplicates(vector<int>& nums)
//{
//	size_t i = 1, j = 0;
//	while (i < nums.size())
//	{
//		if (nums[i] != nums[j])
//			j++;
//		nums[j] = nums[i];
//		i++;
//	}
//	nums.resize(j + 1);
//	return j + 1;
//}
////字母组合
//class Solution
//{
//public:
//	vector<string> letterCombinations(string digits)
//	{
//		vector<string> ret;
//		Combine(digits, 0, ret);
//		return ret;
//	}
//private:
//	string map[8] = { "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" };
//	void Combine(string& digits, int i, vector<string>& ans, string str = "")
//	{
//		if (i == digits.size())		//digit为空字符串时不插入
//			digits.size() ? ans.push_back(str), 1 : 0;
//		else
//		{
//			for (const auto& e : map[digits[i] - '2'])
//				Combine(digits, i + 1, ans, str + e);
//		}
//	}
//};
////找出数组中出现次数超过一半的数
//int MoreThanHalfNum_Solution(vector<int>& numbers)
//{
//	//众数投票法
//	int ret = -1, flag = 0;
//	for (auto e : numbers)
//	{
//		if (flag == 0)
//			ret = e;
//		if (e != ret)
//			flag--;
//		else
//			flag++;
//	}
//	return ret;
//}

#include <list>
//list
//list的介绍
//list是可以在任意位置进行插入和删除的序列式容器,该容器可以前后双向迭代
//底层是带头双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素
//list与forward_list非常相似:	forward_list是单链表,只能朝前迭代,已让其更简单高效
//与其他的序列式容器相比(array	vector	deque),list通常在任意位置进行插入/移除元素的执行效率更好
//			list和forward_list最大的缺陷是不支持任意位置的随机访问
//				访问list的第n个元素,必须从已知的位置(如头/尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销
//list还需要一些额外的空间,以保存每个节点的相关联信息
//list的定义
//std::list
//template < class T, class Alloc = allocator<T> > class list;
//
//void List_List()
//{
//	cout << "List_List" << endl;
//	list<int> lt;
//	for (int i = 1; i <= 5; i++)
//		lt.push_back(i);
//
//	list<int>::iterator it = lt.begin();
//	while (it != lt.end())
//	{
//		cout << *it << ' ';
//		*it *= 2;
//		it++;
//	}
//	cout << endl;
//
//	for (const auto& e : lt)
//		cout << e << ' ';
//	cout << endl << endl;
//}
//void List_Modify()
//{
//	cout << "List_Modify" << endl;
//	list<int> lt;
//	for (int i = 1; i <= 5; ++i)
//		lt.push_back(i);
//
//	list<int>::iterator pos = find(lt.begin(), lt.end(), 3);
//	if (pos != lt.end())
//		pos = lt.insert(pos, 30);		//list的insert不存在迭代器失效的问题,可以不接收返回值
//	cout << *pos << endl;
//	for (auto e : lt)
//		cout << e << ' ';
//	cout << endl;
//
//	pos = find(lt.begin(), lt.end(), 3);
//	if (pos != lt.end())
//		pos = lt.erase(pos);
//	cout << *pos << endl;
//	for (auto e : lt)
//		cout << e << ' ';
//	cout << endl << endl;
//}
//int main()
//{
//	List_List();
//	List_Modify();
//	return 0;
//}
//
//测试实现list
#include "list.h"
namespace qyc
{
	namespace test
	{
		void List_List()
		{
			cout << "List_List" << endl;

			list<int> lt;
			for (int i = 1; i <= 5; ++i)
				lt.push_back(i);
			lt.push_back(5);
			lt.pop_back();

			list<int>::iterator it = lt.begin();
			while (it != lt.end())
				cout << *it++ << ' ';
			cout << endl;
			for (auto e : list<int>(lt))
				cout << e << ' ';
			cout << endl;

			list<int> lt1 = lt;
			for (auto e : lt1)
				cout << e << ' ';
			cout << endl;

			list<int> lt2(5, 1);
			for (auto e : lt2)
				cout << e << ' ';
			cout << endl;

			list<int> lt3(lt1.begin(), lt1.end());
			for (auto e : lt3)
				cout << e << ' ';
			cout << endl;
			lt3.erase(++lt3.begin(), --lt3.end());
			for (auto e : lt3)
				cout << e << ' ';
			cout << endl;

			lt3.clear();
			cout << lt3.size() << ' ';
			lt3.push_back(3);
			cout << *lt3.begin() << endl;

			lt3 = lt1;
			for (auto e : lt3)
				cout << e << ' ';
			cout << endl << endl;
		}
		void List_Modify()
		{
			cout << "List_Modify" << endl;
			list<int> lt;
			for (int i = 1; i <= 5; ++i)
				lt.push_back(i);

			list<int>::iterator pos = lt.begin();//find(lt.begin(), lt.end(), 3);
			if (pos != lt.end())
			{
				pos = lt.insert(pos, 30);
				pos = lt.insert(pos, 40);
				pos = lt.insert(pos, 50);
			}
			for (auto e : lt)
				cout << e << ' ';
			cout << endl;
			pos = lt.erase(pos);
			pos = lt.erase(pos);
			pos = lt.erase(pos);
			for (auto e : lt)
				cout << e << ' ';
			cout << endl;
			lt.reverse();
			for (auto e : lt)
				cout << e << ' ';
			cout << endl;
			list<int>::reverse_iterator it = lt.rbegin();
			while (it != lt.rend())
			{
				cout << *it << ' ';
				++it;
			}
			cout << endl;
			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		qyc::test::List_List();
//		qyc::test::List_Modify();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//
//	return 0;
//}

#include <stack>
#include <queue>
#include <deque>
//stack & queue & priority_queue
//stack
//stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中
//		只能从容器的一端进行元素的插入与提取操作
//stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器
//		stack提供一组特定的成员函数来访问其元素,元素从特定容器的尾部(即栈顶)被压入和弹出
//stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类，这些容器类应该支持以下操作:
//		empty:判空	back:获取尾部元素	push_back:尾插	pop_back:尾删
//标准容器vector deque list均符合这些需求
//		如果没有为stack指定特定的底层容器,默认使用使用deque
//stack的实现
//template <class T, class Container = deque<T>> class stack;
//stack的构造函数
//explicit stack (const container_type& ctnr = container_type());
//
//queue
//队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作
//		只能从容器一端插入元素，另一端提取元素
//队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类
//		queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列
//底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类
//该底层容器应至少支持以下操作:
//		empty:判空	size:返回队列中有效元素的个数	front:返回队头元素的引用	back:返回队尾元素的引用	push_back:入队列	pop_front:出队列
//标准容器类deque和list满足了这些要求
//		如果没有为queue实例化指定容器类,默认使用标准容器deque
//queue的实现
//template <class T, class Container = deque<T>> class queue;
//queue的构造函数
//explicit queue (const container_type& ctnr = container_type());
//
//容器适配器
//适配器:  一种设计模式  (设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的总结)
//         该种模式是将一个类的接口转换成用户希望的另外一个接口
//虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器
//      因为stack和queue只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque
// 
//deque的简单介绍
//deque(双端队列):  一种双开口的"连续"空间的数据结构
//      双开口的含义是:可以在头尾两端进行插入和删除操作且时间复杂度为O(1)
//                      与vector比较,头插效率高,不需要搬移元素     与list比较,空间利用率比较高,且支持随机访问
//deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的(存储一个指针数组管理一段段的小空间)
//      实际deque类似于一个动态的二维数组
//双端队列底层是一段假象的连续空间,实际是分段连续的
//为了维护其"整体连续"以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂
//deque的优势
//与vector比较
//      头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是比vector高的
//与list比较
//      其底层是连续空间,空间利用率比较高,不需要存储额外字段
//deque有一个致命缺陷:
//      不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下
//                              而序列式场景中,可能需要经常遍历
//因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多
//              而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构
//stack是后进先出的特殊线性数据结构,只要具有push_back和pop_back操作的线性结构,都可以作为stack的底层容器
//      如vector和list都可以
//queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器
//      如list
//在STL中对stack和queue默认选择deque作为其底层容器主要是因为
//stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作
//stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据)
//queue中的元素增长时,deque不仅效率高,而且内存使用率高
//结合了deque的优点，而完美的避开了其缺陷
// 
//priority_queue
//优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的
//        此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)
//优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类
//priority_queue提供一组特定的成员函数来访问其元素
//元素从特定容器的"尾部"弹出,其称为优先队列的顶部
//底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类
//容器应该可以通过随机访问迭代器访问,并支持以下操作:
//      empty:判空    size:有效元素个数    front:容器中第一个元素    push_back:尾插
//标准容器类vector和deque满足这些需求
//      若没有为特定的priority_queue类实例化指定容器类,默认使用vector
//需要支持随机访问迭代器,以便始终在内部保持堆结构
//容器适配器通过在需要时自动调用算法函数make_heap,push_heap,pop_heap来自动完成此操作
//priority_queue的使用
//优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构
//      因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue
//注意:默认情况下priority_queue是大堆
//priority_queue
//template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type>> class priority_queue;
//
//void Stack_Stack()
//{
//	cout << "Stack_Stack" << endl;
//
//	stack<int> st;
//	for (int i = 1; i <= 5; ++i)
//		st.push(i);
//
//	while (!st.empty())
//	{
//		cout << st.top() << ' ';
//		st.pop();
//	}
//	cout << endl;
//	cout << endl;
//}
//void Queue_Queue()
//{
//	cout << "Queue_Queue" << endl;
//
//	queue<int> st;
//	for (int i = 1; i <= 5; ++i)
//		st.push(i);
//
//	while (!st.empty())
//	{
//		cout << st.front() << ' ';
//		st.pop();
//	}
//	cout << endl;
//	cout << endl;
//}
//void Priority_Queue()
//{
//    cout << "Priority_Queue" << endl;
//
//    priority_queue<int> st;
//    for (int i = 0; i < 5; ++i)
//        st.push(rand() % 100);
//
//    while (!st.empty())
//    {
//        cout << st.top() << ' ';
//        st.pop();
//    }
//    cout << endl;
//
//    priority_queue<int, vector<int>, greater<int>> st1;
//    for (int i = 0; i < 5; ++i)
//        st1.push(rand() % 100);
//
//    while (!st1.empty())
//    {
//        cout << st1.top() << ' ';
//        st1.pop();
//    }
//    cout << endl;
//    cout << endl;
//}
//int main()
//{
//	Stack_Stack();
//	Queue_Queue();
//    Priority_Queue();
//	return 0;
//}
//
//仿函数/函数对象
//是一个类重载operator()
//namespace qyc
//{
//    template<class T>
//    class less
//    {
//    public:
//        bool operator()(const T& l, const T& r) const
//        {
//            return l < r;
//        }
//    };
//    template<class T>
//    class greater
//    {
//    public:
//        bool operator()(const T& l, const T& r) const
//        {
//            return l > r;
//        }
//    };
//    void test()
//    {
//        less<int> IsFunc;            //类对象可以像函数一样使用
//        cout << IsFunc(1, 2) << endl;
//    }
//}
//
//测试实现stack & queue & priority_queue
#include "stack.h"
#include "queue.h"
namespace qyc
{
	namespace test
	{
		void Stack_Stack()
		{
			cout << "Stack_Stack" << endl;

			stack<int, vector<int>> st;
			for (int i = 1; i <= 5; ++i)
				st.push(i);

			while (!st.empty())
			{
				cout << st.top() << ' ';
				st.pop();
			}
			cout << endl;
			cout << endl;
		}
		void Queue_Queue()
		{
			cout << "Queue_Queue" << endl;

			queue<int, list<int>> st;
			for (int i = 1; i <= 5; ++i)
				st.push(i);

			while (!st.empty())
			{
				cout << st.front() << ' ';
				st.pop();
			}
			cout << endl;
			cout << endl;
		}
		void Priority_Queue()
		{
			cout << "Priority_Queue" << endl;

			int arr[] = { 5, 3, 7, 1, 2, 4, 3, 0, 9, 6 };
			priority_queue<int> st(arr, arr + 10);
			//for (int i = 0; i < 10; ++i)
			//    st.push(arr[i]);

			while (!st.empty())
			{
				cout << st.top() << ' ';
				st.pop();
			}
			cout << endl;

			priority_queue<int, vector<int>, greater<int>> st1(arr, arr + 10);
			//for (int i = 0; i < 5; ++i)
			//   st1.push(rand() % 100);

			while (!st1.empty())
			{
				cout << st1.top() << ' ';
				st1.pop();
			}
			cout << endl;
			cout << endl;
		}
	}
}
//int main()
//{
//    try
//    {
//        qyc::test::Stack_Stack();
//        qyc::test::Queue_Queue();
//        qyc::test::Priority_Queue();
//    }
//    catch (const exception& e)
//    {
//        cout << e.what() << endl;
//    }
//    return 0;
//}
//
//stack & queue练习题
////可以完成栈的正常操作,并且可以在O(1)时间内返回栈中最小值
//class MinStack
//{
//public:
//    MinStack()
//    {}
//
//    void push(int val)
//    {
//        _st.push(val);
//        if (_minst.empty() || val <= _minst.top())
//            _minst.push(val);
//    }
//
//    void pop()
//    {
//        if (_st.top() == _minst.top())
//            _minst.pop();
//        _st.pop();
//    }
//
//    int top()
//    {
//        return _st.top();
//    }
//
//    int getMin()
//    {
//        return _minst.top();
//    }
//private:
//    std::stack<int> _st;		//正常栈,正常存储数据
//    std::stack<int> _minst;		//存储最小数据的栈,只有当push进_st的val小于等于_minst的栈顶元素时,_minst才push
//								//当_st的pop值与_minst栈顶元素相等时,_minst才pop
//	//大量重复数据的时候,_minst的类型改为 std::stack<CountVal> ,struct CountValue { int _val; int _count; }
//																//_val存储最小数值,_count存储该数值出现次数
//};
////给出栈的入栈序列,判断给出的出栈序列是否合理
//bool IsPopOrder_1(vector<int>& pushV, vector<int>& popV)
//{
//    stack<int> st;
//    size_t pushi = 0, popi = 0;
//    while (pushi < pushV.size())        //将入栈序列走完
//    {
//        if (pushV[pushi] == popV[popi])
//            ++pushi, ++popi;         //当入栈元素位置和出栈元素位置相等时,可以看做入栈之后立即出栈
//        else
//        {
//            if (!st.empty() && st.top() == popV[popi])
//            {   //当栈不为空时,且出栈元素与栈顶元素相等时,可以看做于此时出栈
//                st.pop();
//                ++popi;
//            }
//            else
//            {
//                st.push(pushV[pushi]);          //都不同时,就模拟入栈
//                ++pushi;
//            }
//        }
//    }
//    while (!st.empty())
//    {   //入栈序列走完后,出栈序列就固定了,将栈顶元素与出栈序列一一匹配
//        if (st.top() != popV[popi])
//            return false;
//        st.pop();
//        ++popi;
//    }
//    return true;
//}
//bool IsPopOrder_2(vector<int>& pushV, vector<int>& popV)
//{
//    stack<int> st;
//    size_t popi = 0;
//    for (auto e : pushV)
//    {
//        st.push(e);
//        while (st.empty() && st.top() == popV[popi])
//        {
//            st.pop();
//            ++popi;
//        }
//    }
//    return st.empty();
//}
////后缀表达式计算,给出一列数字与 + - * / 混合的表达式,计算其结果
////计算顺序为:读取到操作符时,操作符前的两个数字使用该操作符并将结果返回
////例:    tokens = ["2","1","+","3","*"]    ((2 + 1) * 3) = 9
////       tokens = ["4","13","5","/","+"]    (4 + (13 / 5)) = 6
//int evalRPN(vector<string>& tokens)
//{
//    stack<int> st;
//    for (auto& e : tokens)
//    {
//        int flag = 0;
//        if (e == "+" || e == "-" || e == "*" || e == "/")
//        {
//            int right = st.top();
//            st.pop();
//            int left = st.top();
//            st.pop();
//            switch (e[0])
//            {
//            case '+':
//                st.push(left + right);
//                break;
//            case '-':
//                st.push(left - right);
//                break;
//            case '*':
//                st.push(left * right);
//                break;
//            case '/':
//                st.push(left / right);
//                break;
//            default:
//                break;
//            }
//        }
//        else
//            st.push(stoi(e));
//    }
//    return st.top();
//}
////中缀转后缀
//size_t PriorityJudge(const string& op)
//{
//	if (op == "+" || op == "-")
//		return 1;
//	else
//		return 2;
//}
//vector<string> Translate(const vector<string>& token)
//{
//	vector<string> ret;
//	stack<string> st;
//	for (auto it = token.begin(); it != token.end(); ++it)
//	{
//		if (*it == "+" || *it == "-" || *it == "*" || *it == "/")
//		{
//			//当栈不为空,且当当前操作符优先级小于等于栈顶优先级时,栈顶操作符出栈
//			//当栈为空,或当前操作符优先级大于栈顶优先级,继续入栈
//			//每个操作符的优先级只需和附近操作符的优先级进行比较就可得出计算顺序
//			if (!st.empty() && PriorityJudge(*it) <= PriorityJudge(st.top()))
//			{
//				ret.push_back(st.top());
//				st.pop();
//			}
//			st.push(*it);
//		}
//		else if (*it == "(")
//		{
//			//遇到括号时,进行递归计算
//			auto end = it;
//			while (*end != ")")
//				++end;
//			const vector<string> rec = Translate(vector<string>(it + 1, end));
//			ret.insert(ret.end(), rec.begin(), rec.end());
//			it = end;
//		}
//		else
//			ret.push_back(*it);
//	}
//	while (!st.empty())
//	{
//		ret.push_back(st.top());
//		st.pop();
//	}
//	return ret;
//}
////层序遍历
//class Solution {
//	struct TreeNode {
//		int val;
//		TreeNode* left;
//		TreeNode* right;
//		TreeNode() : val(0), left(nullptr), right(nullptr) {}
//		TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
//		TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
//	};
//public:
//    vector<vector<int>> levelOrder(TreeNode* root) {
//        vector<vector<int>> ret;
//        queue<TreeNode*> qu;
//        if (root != nullptr)
//            qu.push(root);
//        while (!qu.empty())
//        {
//            vector<int> v;
//            size_t size = qu.size();
//            for (size_t i = 0; i < size; ++i)
//            {
//                TreeNode* node = qu.front();
//                qu.pop();
//                v.push_back(node->val);
//                if (node->left != nullptr)
//                    qu.push(node->left);
//                if (node->right != nullptr)
//                    qu.push(node->right);
//            }
//            ret.push_back(v);
//        }
//        return ret;
//    }
//	vector<vector<int>> levelOrderBottom(TreeNode* root) {
//		vector<vector<int>> ret = levelOrder(root);
//		reverse(ret.begin(), ret.end());
//		return ret;
//	}
//};

//模板进阶
//
//非类型模板参数
//模板参数分为 类型形参 与 非类型形参
//	类型形参:	出现在模板参数列表中,跟在class或者typename之后的参数类型名称
//	非类型形参:	就是用一个 常量 作为模板的一个参数,在模板中可将该参数当成常量来使用
//例:	template<class T, size_t N = 10>, T 为类型形参, N 为非类型形参
//注意:		浮点数,类对象以及字符串是不允许作为非类型模板参数的
//			非类型的模板参数必须在编译期就能确认结果
//const int _aa = 10;		//只能使用 整形常量 作为模板的非类型形参
//template<class T, size_t N = _aa>
//class Array
//{
//private:
//	T _a[N];
//};
//int main()
//{
//	Array<int> a;
//	Array<int, 100> a1;
//	Array<double, 1000> a2;
//	return 0;
//}
//
#include <array>
//array
//也是采用非类型模板参数
//array定义
//template<class T, size_t N> class array;
//array的使用与正常定义的数组(T a[N])的使用基本相同
//		+ 构造函数	+ 迭代器	+ 越界访问检查
//正常定义的数组:
//		在数组结束位置之后添加几个值,程序运行结束后判断其是否被改变
//			以此来判定是否越界(且当越界多个字节时,就判定不到了)
//	其访问为指针的解引用,只检查越界写,不检查越界读
//array:
//	其访问为函数调用,只要越界都能查出
#include "array.h"
//int main()
//{
//	array<int, 10> a;
//	int n = 0;		//当n输入大于20时,虽然越界写入,但未报错
//	{
//		int a1[10];
//		cin >> n;
//		cout << a1[n] << endl;
//		a1[n] = 0;
//	}
//	a[n];		//无论是读/写都会被检测到
//	while (1);		//当程序结束时判断是否越界
//	return 0;
//}
//
//模板的特化
//通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊的类型可能会得到一些错误的结果,就需要对模板进行特化
//template<class T>
//bool cmp(T left, T right)	{ return left > right };
//int a = 0, b = 1;
//cmp(a, b);		//正确
//int* pa = &a, *pb = b;
//cmp(pa, pb);		//错误
//在原模板类的基础上,针对特殊类型所进行特殊化的实现方式
//		模板特化中分为函数模板特化与类模板特化
//函数模板的特化步骤
//1.	必须要先有一个基础的函数模板
//2.	关键字template后面接一对空的尖括号 < >
//3.	函数名后跟一对尖括号,尖括号中指定需要特化的类型
//4.	函数形参表: 必须要和模板函数的基础参数类型完全相同
//template<>
//bool cmp<int*>(int* left, int* right) { *left > *right };
//一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
//bool cmp(int* left, int* right) { *left > *right };
//实现简单明了,代码的可读性高,易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化
//
//类模板特化
//全特化
//全特化:	将模板参数列表中所有的参数都确定化
//template<class T1, class T2>
//class Data { T1 _d1;	T2 _d2; };
//template<>
//class Data<int, char> { int _d1; char _d2 };
//偏特化
//偏特化:	针对模版参数进一步条件限制设计的特化版本
//偏特化有以下两种表现方式:
//部分特化:	将模板参数类表中的一部分参数特化
//template <class T1>
//class Data<T1, int> { T1 _d1; int _d2 };
//参数更进一步的限制
//偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//template <typename T1, typename T2>
//class Date<T1*, T2*> { T1 _d1; T1 _d2 };
//class Date
//{
//public:
//	Date(size_t year, size_t month, size_t day)
//		: _year(year)
//		, _month(month)
//		, _day(day)
//	{}
//	bool operator<(const Date& x) const
//	{
//		if (_year < x._year)
//			return true;
//		if (_month < x._month)
//			return true;
//		return _day < x._day;
//	}
//	size_t _year;
//	size_t _month;
//	size_t _day;
//};
////函数模板特化
//template<class T>
//bool Less(T left, T right)
//{
//	return left < right;
//}
//template<>		//对其进行特化
//bool Less<Date*>(Date* left, Date* right)
//{
//	return *left < *right;
//}
////类模板特化
//namespace qyc
//{
//	template <class T>
//	struct less
//	{
//		bool operator()(const T& x1, const T& x2) const
//		{
//			return x1 < x2;
//		}
//	};
//
//	template<class T>
//	struct less<T*>
//	{
//		bool operator()(Date*& x1, Date*& x2) const
//		{
//			return *x1 < *x2;
//		}
//	};
//}
//int main()
//{
//	cout << Less(2, 1) << endl;
//
//	Date d1(2023, 1, 1);
//	Date d2(2023, 1, 2);
//	cout << Less(d1, d2) << endl;
//
//	Date* p1 = &d1;
//	Date* p2 = &d2;
//	cout << Less(p1, p2) << endl;
//
//	qyc::less<Date> lessFunc1;
//	cout << lessFunc1(d1, d2) << endl;
//
//	qyc::less<Date*> lessFunc2;
//	cout << lessFunc2(p1, p2) << endl;
//
//	priority_queue<Date, vector<Date>, qyc::less<Date>> dq1;
//	for (int i = 0; i < 5; ++i)
//		dq1.push(Date(2023, 1, 1 + i));
//	while (!dq1.empty())
//	{
//		Date tmp = dq1.top();
//		cout << tmp._year << '/' << tmp._month << '/' << tmp._day << endl;
//		dq1.pop();
//	}
//
//	cout << endl;
//	
//	priority_queue<Date*, vector<Date*>, qyc::less<Date*>> dq2;
//	for (int i = 0; i < 5; ++i)
//		dq2.push(new Date(2023, 1, 1 + i));
//	while (!dq2.empty())
//	{
//		Date* tmp = dq2.top();
//		cout << tmp->_year << '/' << tmp->_month << '/' << tmp->_day << endl;
//		delete tmp;
//		dq2.pop();
//	}
//
//	return 0;
//}
//偏特化
//C++中函数模板不支持偏特化,只有类模板可以进行偏特化
//template<class T1, class T2>
//class Date
//{
//public:
//	Date() { cout << "Date<T1, T2>" << endl; };
//private:
//	T1 _d1;
//	T2 _d2;
//};
////指定具体的类型进行特化
//template<>
//class Date<int, int>
//{
//public:
//	Date() { cout << "Date<int, int>" << endl; };
//private:
//	//可写可不写
//	//int _d1;
//	//int _d2;
//};
//template<class T1>
//class Date<T1, char>
//{
//public:
//	Date() { cout << "Date<T1, char>" << endl; };
//};
////对模板参数的类型进行限制
//template<class T1, class T2>
//class Date<T1*, T2*>
//{
//public:
//	Date() { cout << "Date<T1*, T2*>" << endl; };
//};
//template<class T1, class T2>
//class Date<T1&, T2&>
//{
//public:
//	Date() { cout << "Date<T1&, T2&>" << endl; };
//};
////直接对类进行显示实例化
//template
//Date<double, double>;
//int main()
//{
//	Date<char, int> a;
//	Date<int, char> b;
//	Date<int, int> c;
//	Date<int*, int*> d;
//	Date<int&, int&> e;
//	Date<double, double> f;
//	return 0;
//}
//
//模板分离编译
//分离编译:	一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件
//C/C++程序要运行,一般要经历以下步骤
//	预处理->编译->汇编->链接
//编译:	对程序按照语言特性进行词法,语法,语义分析,错误检查无误后,再生成汇编代码
//			头文件不参与编译,编译器对工程中的多个源文件是分开单独编译的
//链接:	将多个.o文件合并成一个,并处理没有解决的地址问题
//当模板定义与声明分离且当其他源文件使用该模板时
//		在模板实现的.cpp中,编译器没有看到对模板的实例化,因此不会生成具体的模板类/函数
//		而在使用了该模板的.cpp生成的.o中,编译器(检查到声明)在链接期间才会找其地址,但模板并未实例化出具体代码,因此链接时报错
//解决方法:
//	将声明和定义放到一个文件xxx.hpp里面或者xxx.h其实也是可以的。推荐使用这种。
//	模板定义的位置显式实例化	这种方法不实用,不推荐使用
//显式实例化:
//	template
//	模板<类型>;
//
//模板总结
//优点:
//	模板复用了代码，节省资源，更快的迭代开发，C++的标准模板库(STL)因此而产生
//	增强了代码的灵活性
//缺陷:
//	模板会导致代码膨胀问题，也会导致编译时间变长
//	出现模板编译错误时，错误信息非常凌乱，不易定位错误

//typename
//typename在模板参数的地方是作为定义模板类型的关键字
//		template<typename T>	作用同class相同
//而在一个类型前加typename是为了告知编译器,其为一个类型
//使用的场景前提:
//	指定一个还未实例化的模板,取该类模板中的内嵌类型
//当模板声明定义分离时,编译器无法区分qyc::vector<T>::iterator是静态成员变量,还是内嵌类型(两者调用访问的方式相同)
//使用typename修饰可以告知编译器,它是一个类型
//template<typename T>
//typename qyc::vector<T>::iterator qyc::vector<T>::insert(typename qyc::vector<T>::iterator pos, const T& x);

//C++进阶
//继承的概念
//继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段
//	它允许程序员在保持原有类特性的基础上进行扩展,增加功能
//		这样产生新的类称派生类
//继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程
//以前接触的复用都是函数复用,继承是类设计层次的复用
//继承格式
//	A:派生类/子类	public:继承方法		B:基类/父类
//	class A : public B
//	{}
//继承关系和访问限定符
//					public继承								public访问
//		继承方式:	protected继承			访问限定符:		protected访问
//					private继承								private访问
//继承类成员访问方式的变化
//	类成员\继承方法			public继承				protected继承			private继承
//	基类的public成员		派生类的public成员		派生类的protected成员	派生类的private成员
//	基类的protected成员		派生类的protected成员	派生类的protected成员	派生类的private成员
//	基类的private成员		派生类中不可见			派生类中不可见			派生类中不可见
//私有成员的意义: 不想被子类继承的成员
//总结:
//1.	基类private成员在派生类中无论以什么方式继承都是不可见的
//				这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类内还是类外都不能访问到它
//2.	基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected
//				可以看出保护成员限定符是因继承才出现的
//3.	基类的私有成员在子类都是不可见,基类的其他成员在子类的访问方式 = Min(成员在基类的访问限定符与继承方式中权限最小的)	public > protected > private
//4.	使用关键字class时默认的继承方式是private	使用struct时默认的继承方式是public
//				不过最好显示的写出继承方式
//5.	在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承
//		也不提倡使用protetced/private继承
//				因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
//class Person
//{
//public:
//	void Print()
//	{
//		cout << "name:" << _name << endl;
//		cout << "age:" << _age << endl;
//	}
//protected:
//	string _name = "peter"; // 姓名
//private:
//	int _age = 18; // 年龄
//};
//class Student : public Person
////class Student : protected Person
////class Student : private Person
//{
//protected:
//	int _stuid; // 学号
//};
//class Teacher : public Person
//{
//protected:
//	int _jobid; // 工号
//};
//int main()
//{
//	Student s;
//	s.Print();
//	
//	return 0;
//}
//
//基类和派生类对象赋值转换
//	派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用
//		这里有个形象的说法叫切片或者切割,把派生类中父类那部分切来赋值过去
//基类对象不能赋值给派生类对象
//基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用
//	但是必须是基类的指针是指向派生类对象时才是安全的
//这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast来进行识别后进行安全转换
//class Person
//{
//protected:
//	string _name; // 姓名
//	string _sex; // 性别
//	int _age; // 年龄
//};
//class Student : public Person
//{
//public:
//	int _No; // 学号
//};
//int main()
//{
//	Student sobj;
//	//1.子类对象可以赋值给父类对象/指针/引用
//	//虽然是不同类型,但不是隐式类型转换
//	//C++语法的一个特殊支持
//	Person pobj = sobj;
//	Person* pp = &sobj;
//	Person& rp = sobj;
//	//2.基类对象不能赋值给派生类对象
//	sobj = pobj;
//	//3.基类的指针可以通过强制类型转换赋值给派生类的指针
//	Student* ps1 = (Student*)pp;
//	Student& rs1 = (Student&)rp;
//	ps1->_No = 10;
//	rs1._No = 20;
//
//	pp = &pobj;
//	Student* ps2 = (Student*)pp; //虽然可以转换,但存在越界访问
//	ps2->_No = 10;
//	return 0;
//}
//
//继承中的作用域
//在继承体系中基类和派生类都有独立的作用域
//	子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义
//		在子类成员函数中,可以使用 基类::基类成员 显示访问
//需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
//注意在实际中在继承体系里面最好不要定义同名的成员
//继承体系中,子类会继承父类的大小,按其结构体对其原则对其空间
//class Person
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//protected:
//	string _name = "李"; // 姓名
//	int _num = 111; // 身份证号
//};
////Person中的fun和Student中的fun不是构成重载,因为不是在同一作用域
////Person中的fun和Student中的fun构成隐藏,成员函数满足函数名相同就构成隐藏
//class Student : public Person
//{
//public:
//	void fun(int i)
//	{
//		Person::fun();
//		cout << "func(int i)->" << i << endl;
//	}
//	void Print()
//	{
//		cout << "姓名:" << _name << endl;
//		cout << "身份证号:" << Person::_num << endl;
//		cout << "学号:" << _num << endl;
//	}
//protected:
//	int _num = 999; // 学号
//};
//int main()
//{
//	Student s1;
//	s1.Print();
//	s1.fun(1);
//	s1.Person::fun();
//	//继承体系中,子类会继承父类的大小,按其结构体对其原则对其空间
//	cout << sizeof(Person) << endl;
//	cout << sizeof(Student) << endl;
//	cout << sizeof(string) << endl;
//	return 0;
//}
//
//派生类的默认成员函数
//6个默认成员函数,"默认"就是指用户不写,编译器会变我们自动生成
//在派生类中
//构造函数:	派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员
//				如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用
//拷贝构造:	派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
//赋值拷贝:	派生类的operator=必须要调用基类的operator=完成基类的复制
//析构函数:	派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员
//				因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序
//派生类对象初始化先调用基类构造再调派生类构造
//			析构清理先调用派生类析构再调基类的析构
//															基类构造函数      |
//	基类构造函数	<--		派生类构造函数					派生类构造函数    |
//	基类行为				派生类行为			---->		派生类行为        |
//	基类析构函数	<--		派生类析构函数					派生类析构函数    |
//															基类构造函数     \|/
//因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同
//编译器会对析构函数名进行特殊处理,处理成destrutor()
//	所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系
//一个类不想被继承,可以将构造函数私有化
//		该类在被继承的时候不会报错,但子类实例化的时候会报错
//在C++11中,新增了关键字final,修饰一个类不能被继承
//class A final {};
//
//继承与友元
//友元关系不能继承,基类友元不能访问子类私有和保护成员
//
//继承与静态成员
//基类定义了static静态成员,则整个继承体系里面只有一个这样的成员
//		无论派生出多少个子类,都只有一个static成员实例
//
//class Student;
//class Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);
//
//	static int _count; // 统计人的个数
//
//	Person(const char* name = "peter")
//		: _name(name)
//	{
//		++_count;
//		cout << "Person()" << endl;
//	}
//	Person(const Person& p)
//		: _name(p._name)
//	{
//		++_count;
//		cout << "Person(const Person& p)" << endl;
//	}
//	Person& operator=(const Person& p)
//	{
//		cout << "Person operator=(const Person& p)" << endl;
//		if (this != &p)
//			_name = p._name;
//		return *this;
//	}
//	~Person()
//	{
//		cout << "~Person()" << endl;
//	}
//protected:
//	string _name; // 姓名
//};
//int Person::_count = 0;
//class Student : public Person
//{
//public:
//	Student(const char* name, int num)
//		: Person(name)
//		, _num(num)
//	{
//		cout << "Student()" << endl;
//	}
//	Student(const Student& s)
//		: Person(s)
//		, _num(s._num)
//	{
//		cout << "Student(const Student& s)" << endl;
//	}
//	Student& operator=(const Student& s)
//	{
//		cout << "Student& operator= (const Student& s)" << endl;
//		if (this != &s)
//		{
//			Person::operator=(s);
//			_num = s._num;
//		}
//		return *this;
//	}
//	~Student()
//	{
//		cout << "~Student()" << endl;
//	}
//protected:
//	int _num; //学号
//};
//void Display(const Person& p, const Student& s)
//{
//	cout << p._name << endl;
//	cout << s.Person::_name << endl;
//	//cout << s._num << endl;			//不能访问
//}
//class A
//{
//public:
//	A()
//		: a(int())
//	{}
//	int a;
//};
//int main()
//{
//	Student s1("jack", 18);
//	Student s2(s1);
//	Student s3("rose", 17);
//	s1 = s3;
//	Display(s1, s2);
//	Person p;
//	cout << p._count << endl;
//	cout << s1._count << endl;
//	s2._count = 0;
//	cout << p._count << endl;
//	cout << s1._count << endl;
//	return 0;
//}
//
//复杂的菱形继承及菱形虚拟继承
//单继承:	一个子类只有一个直接父类,	称该继承关系为单继承
//			class Person
//			class Student : public Person
//			class PostGraduate : public Student
//多继承:	一个子类有两个或以上直接父类	称该继承关系为多继承
//			class Student		class Teacher
//		class Assistant : public Student, public Teacher
//菱形继承:	菱形继承是多继承的一种特殊情况
//								class Person
//		class Student : public Person		class Teacher : public Person
//				class Assistant : public Student, public Teacher
//菱形继承的问题:
//		从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题
//在Assistant的对象中Person成员会有两份
//					string _name	---> Person
//					int _num						---> Student
//
//					string _name	---> Person
//					int _id							---> Teacher
//
//					string _majorCourse								---> Assistant
//class Person
//{
//public:
//	string _name; // 姓名
//};
//class Student : public Person
//{
//protected:
//	int _num; //学号
//};
//class Teacher : public Person
//{
//protected:
//	int _id; // 职工编号
//};
//class Assistant : public Student, public Teacher
//{
//protected:
//	string _majorCourse; // 主修课程
//};
//int main()
//{
//	Assistant a;
//	//a._name = "peter";	//有二义性无法明确知道访问的是哪一个
//	//需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
//	a.Student::_name = "xxx";
//	a.Teacher::_name = "yyy";
//	return 0;
//}
//虚拟继承解决数据冗余和二义性的原理
//可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C
//这里是通过了B和C的两个指针,指向的一张表
//		这两个指针叫虚基表指针,这两个表叫虚基表
//虚基表中存的偏移量,通过偏移量可以找到下面的A
//		    -----	A的偏移量地址
//		   |		int _b				---> B
//		A偏移量
//		   |-----	A的偏移量地址
//		   |		int _c				---> C
//		A偏移量
//		   |		int _d				---> D
//		    --->	int _a				---> A
//class A
//{
//public:
//	int _a;
//};
////class B : public A
//class B : virtual public A
//{
//public:
//	int _b;
//};
////class C : public A
//class C : virtual public A
//{
//public:
//	int _c;
//};
//class D : public B, public C
//{
//public:
//	int _d;
//};
//int main()
//{
//	D d;
//	d.B::_a = 1;
//	d.C::_a = 2;
//	d._b = 3;
//	d._c = 4;
//	d._d = 5;
//	return 0;
//}
//
//继承的总结
//多继承就是C++语法复杂的体现
//	有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂
//		一般不建议设计出多继承,一定不要设计出菱形继承(否则在复杂度及性能上都有问题)
//	多继承可以认为是C++的缺陷之一,很多后来的语言都没有多继承,如Java
//继承和组合
//public继承是一种is-a的关系	每个派生类对象都是一个基类对象
//组合是一种has-a的关系		假设B组合了A,每个B对象中都有一个A对象
//		优先使用对象组合,而不是类继承
//继承允许根据基类的实现来定义派生类的实现
//	这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)
//		术语"白箱"是相对可视性而言:	在继承方式中,基类的内部细节对子类可见
//继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响
//		派生类和基类间的依赖关系很强,耦合度高
//对象组合是类继承之外的另一种复用选择
//新的更复杂的功能可以通过组装或组合对象来获得,对象组合要求被组合的对象具有良好定义的接口
//这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的	对象只以"黑箱"的形式出现
//		组合类之间没有很强的依赖关系,耦合度低	优先使用对象组合有助于保持每个类被封装
//	实际尽量多去用组合,组合的耦合度低,代码维护性好
//不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承
//类之间的关系可以用继承,可以用组合就用组合
//// Car和BMW Car和Benz构成is-a的关系
//class Car {
//protected:
//	string _colour = "白色"; // 颜色
//	string _num = "000"; // 车牌号
//};
//class BMW : public Car {
//public:
//	void Drive() { cout << "好开-操控" << endl; }
//};
//class Benz : public Car {
//public:
//	void Drive() { cout << "好坐-舒适" << endl; }
//};
//
//// Tire和Car构成has-a的关系
//class Tire {
//protected:
//	string _brand = "Michelin"; // 品牌
//	size_t _size = 17; // 尺寸
//};
//class Car {
//protected:
//	string _colour = "白色"; // 颜色
//	string _num = "000"; // 车牌号
//	Tire _t; // 轮胎
//};

//多态
//多态的概念:
//		多种形态,完成某个行为,当不同的对象去完成时会产生出不同的状态
//多态的构成条件:
//		多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为
//在继承中要构成多态还有两个条件:
//	必须通过基类的指针或者引用调用虚函数		<-------------重点
//	被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
//虚函数:	被virtual修饰的类成员函数
//虚函数的重写(覆盖):
//	派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型,函数名字,参数列表完全相同)
//		称子类的虚函数重写了基类的虚函数
//注意:		
//	在重写基类虚函数时,派生类的虚函数在不加virtual关键字,也可以构成重写
//			因为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性
//		但是该种写法不是很规范,不建议这样使用
//虚函数重写的两个例外:
//协变		(基类与派生类虚函数返回值类型不同	但必须是父子关系的指针或者引用)
//	派生类重写基类虚函数时,与基类虚函数返回值类型不同
//		即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变
//析构函数的重写		(基类与派生类析构函数的名字不同)
//		如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写
//		虽然基类与派生类析构函数名字不同,看起来违背了重写的规则,其实不然
//			可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor
//虚函数重写是接口继承	直接使用父类的虚函数首部(包括缺省值)
//						继承父类的接口,只重写了实现部分(即函数体)
//	所以派生类的虚函数在实现时,无论是否使用virtual修饰都一样
//普通函数继承是实现继承
//接口继承 & 实现继承
//普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现
//虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态
//		继承的是接口,所以如果不实现多态,不要把函数定义成虚函数
//class A
//{
//public:
//
//};
//class B : public A
//{
//public:
//
//};
//class Person
//{
//public:
//	virtual void BuyTicket(int val = 0)
//	{
//		cout << val << "OverHead price" << endl;
//	}
//	virtual A* Test()
//	{
//		cout << "A* Person" << endl;
//		return nullptr;
//	}
//	virtual ~Person()
//	{
//		cout << "~Person" << endl;
//	}
//};
//class Student : public Person
//{
//public:
//	virtual void BuyTicket(int val = 1)
//	{
//		cout << val << "Half Price" << endl;
//	}
//	virtual B* Test()
//	{
//		cout << "B* Student" << endl;
//		return nullptr;
//	}
//	virtual ~Student()
//	{
//		cout << "~Student" << endl;
//	}
//};
//class Soldier : public Person
//{
//public:
//	virtual void BuyTicket(int val = 3)
//	{
//		cout << val << "Proirity Buy" << endl;
//	}
//	virtual ~Soldier()
//	{
//		cout << "~Soldier" << endl;
//	}
//};
//void BuyOperator(Person& man)		//只能是由父类的指针或者引用调用虚函数
//{
//	man.BuyTicket();
//}
//class AA
//{
//public:
//	virtual void func(int val = 1)
//	{
//		cout << "A->" << val << endl;
//	}
//	virtual void test()
//	{
//		func();
//	}
//};
//class BB : public AA
//{
//public:
//	virtual void func(int val = 0)
//	{
//		cout << "B->" << val << endl;
//	}
//};
//int main()
//{
//	Person a1;
//	BuyOperator(a1);
//	Student a2;
//	BuyOperator(a2);
//	Soldier a3;
//	BuyOperator(a3);
//
//	Person* p1 = new Person;
//	Person* p2 = new Student;
//	Person* p3 = new Soldier;
//	p1->Test();
//	p2->Test();
//	cout << endl;
//
//	delete p1;
//	cout << endl;
//	delete p2;
//	cout << endl;
//	delete p3;
//	cout << endl;
//
//	BB* pbb = new BB;
//	pbb->test();
//	cout << endl;
//	return 0;
//}
//override & final (C++11)
//C++对函数重写的要求比较严格,但是有些情况下可能会导致函数名字母次序写反而无法构成重载
//		而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失
//因此:	C++11提供了 override 和 final 两个关键字
//			可以帮助用户检测是否重写
//final:	修饰虚函数,表示该虚函数不能再被重写
//override:	检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
//class Car
//{
//public:
//	virtual void Drive() final
//	{}
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive()	//直接报错,无法重写函数
//	{
//		cout << "Benz-舒适" << endl;
//	}
//};
//class Car
//{
//public:
//	virtual void Drive()
//	{}
//};
//class Benz : public Car
//{
//public:
//	virtual void Drive() override	//若该函数没有重写,则直接编译报错
//	{
//		cout << "Benz-舒适" << endl;
//	}
//};
//重载	覆盖(重写)	隐藏(重定义)的对比
//	重载:			两个函数在同一作用域函数名相同,参数不同
//	重写(覆盖):		两个函数分别在基类和派生类的作用域中,函数名,参数,返回值都必须相同,且两个函数必须是虚函数
//	重定义(隐藏):	两个函数分别在基类和派生类的作用域中,函数名相同,且没有构成重写
//
//抽象类
//	在虚函数的后面写上 = 0 ,则这个函数为纯虚函数
//包含纯虚函数的类叫做抽象类(也叫接口类)
//抽象类不能实例化出对象,派生类继承后也不能实例化出对象
//		只有重写纯虚函数,派生类才能实例化出对象
//纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承
//class Car
//{
//public:
//	virtual void Drive() = 0;
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive()
//	{
//		cout << "Benz-舒适" << endl;
//	}
//};
//class BMW :public Car
//{
//public:
//	virtual void Drive()
//	{
//		cout << "BMW-操控" << endl;
//	}
//};
//int main()
//{
//	Car* pBenz = new Benz;
//	pBenz->Drive();
//
//	Car* pBMW = new BMW;
//	pBMW->Drive();
//	
//	return 0;
//}
//
//多态的原理
//虚函数表		子类切片赋值给父类时,虚表不进行拷贝
//除了成员变量,还包含一个__vfptr放在对象的前面(Windows平台下)
//		对象中的这个指针叫做虚函数表指针(v代表virtual,f代表function)
//一个含有虚函数的类中都至少有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中
//		虚函数表也简称虚表
//派生类对象中也有一个虚表指针
//	派生类对象由两部分构成:		父类继承下来的成员和虚表指针
//								自身的成员变量
//基类对象和派生类对象的虚表是不一样的
//	当派生类对象中的虚函数完成了重写后,派生类的虚表中存的是重写的 派生类::虚函数
//		所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖
//			重写是语法的叫法,覆盖是原理层的叫法
//另外基类的其他虚函数继承下来后仍是虚函数,仍放进虚表
//		只有被重写的虚函数才会被覆盖,未被重写的虚函数不会被覆盖
//	普通函数继承下来,但是不是虚函数,所以不会放进虚表
//虚函数表本质是一个存虚函数指针的指针数组
//		一般情况这个数组最后面放了一个nullptr
//派生类的虚表生成:	
//	先将基类中的虚表内容拷贝一份到派生类虚表中
//	如果派生类重写了基类中某个虚函数,派生类自己的虚函数覆盖虚表中基类的虚函数
//	派生类中新增的虚函数按其在派生类中的声明次序增加到派生类虚表的最后
//	在初始化列表阶段生成,当一个基类构造完成之后立刻将重写该基类的虚函数进行覆盖,再顺序构造其他基类
//	析构阶段销毁,先析构派生类,再将被派生类重写的基类虚函数重新写回,再去析构基类
//注意:
//	虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的
//		只是他的指针又存到了虚表中
//	对象中存的不是虚表,存的是虚表指针
//多态的原理
//	当基类指针或引用指向派生类时,该指针指向内容的虚表中存的是派生类重写的虚函数
//		故表象上看是指向的是同一个函数,实则指向不同的函数地址
//			//前提:		B : public A		Func1为已重写的虚函数	Func2为未重写的虚函数	Func3为普通函数
//			A* pa = new B;		--->		B:		__vfptr:  		0x00007ff65c0bbf70 {C++Learn.exe!void(*派生类::vftable[n])}{...} (n为虚函数个数+1)
//			A a;										0x00000000	0x00007ff66af41168 {C++Learn.exe!派生类::Func1(参数列表)}
//														0x00000001	0x00007ff66af414d3 {C++Learn.exe!基类::Func2(参数列表)}
//														......
//			pa->Func1(参数);			//实际调用的是派生类重写的虚函数
//					mov         rax, qword ptr [pa]		执行后rax中存储派生类对象的地址(由于__vfptr是派生类对象的第一个元素,故也是__vfptr的地址)
//					mov         rax, qword ptr [rax]	执行后rax中存储派生类对象的__vfptr
//					mov         rcx, qword ptr [pa]		执行后获取派生类对象的地址,用于隐式传参给this指针
//					call        qword ptr [rax]			call __vfptr[0](即第一个虚函数的地址)
//			a.Func1(参数);			//调用的是基类的虚函数
//			a.Func3(参数);
//					lea         rcx, [a]
//					call        基类::Func3(07FF619A418F2h)		直接获取函数地址call调用
//	就实现出了不同对象去完成同一行为时,展现出不同的形态
//	故要达到多态,有两个条件,一个是虚函数覆盖,一个是基类对象的指针或引用调用虚函数
//满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的
//而不满足多态的函数调用则是编译时确认好的
//		普通函数的调用转换成地址,是在编译时(或者链接时)从符号表确认了函数的地址
// 
//动态绑定 & 静态绑定
//静态绑定又称为前期绑定(早绑定)
//	在程序编译期间确定了程序的行为,也称为静态多态		如:函数重载
//动态绑定又称后期绑定(晚绑定)
//	是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态
//
//多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中
//菱形继承的派生类中重复定义基类的虚函数表
//虚拟菱形继承时,继承的父类没有独立的虚函数时,派生类中只有一个虚函数表,其结构排布不发生特殊变化,虚拟继承的基类仍放在最后
//						有自己的独立虚函数时,派生类有多个虚函数表,派生类自身的虚函数仍放入第一个位置的虚函数表中
//		派生类的基类中第一个位置放置虚函数表,第二个位置放置虚基表
//												虚拟继承中的虚基表中前四个字节存储与虚函数表的偏移量,后四个字节存与虚拟继承基类的偏移量
//			//前提:		C : public A, public B		Func1为A,B中互相隐藏的重写虚函数
//			//每个类中均有一个int类型成员变量,即A,B类大小为16Byte,C类大小为24Byte
//			//			Func2为A的未重写虚函数		Func3为B的未重写虚函数		Func4为C的虚函数
//			C c;		--->		c:		A::__vfptr:		0x00007ff61b05c030{C++Learn.exe!void(*C::vftable[4])()}{...}
//			A* pa = &c;							0x00000000	0x00007ff61b041069{C++Learn.exe!void(*C::Func1())}
//			B* pb = &c;							0x00000001	0x00007ff61b0411b3{C++Learn.exe!void(*A::Func2())}
//												0x00000002	0x00007ff61b041951{C++Learn.exe!void(*C::Func4())}
//												0x00000003	0x0000000000000000
//											B::__vfptr:		0x00007ff681c1d098{C++Learn.exe!void(*C::vftable[3])()}{...}
//												0x00000000	0x00007ff681c011d1{C++Learn.exe![thunk]:C::Func1`adjustor{16}(void)}
//																			//多继承中,派生类重写了多个父类互相隐藏的虚函数时,只有最上方的父类会存储虚函数的跳转地址
//												0x00000001	0x00007ff681c013cf{C++Learn.exe!void(*B::Func3())}
//												0x00000002	0x0000000000000000
//			pa->Func1();
//					mov         rax,qword ptr [pa]
//					mov         rax, qword ptr [rax]
//					mov         rcx, qword ptr [pa]
//					call        qword ptr [rax]
//00007FF681C01069	jmp         C::Func1 (07FF681C06A60h)		//直接跳转至Func1函数的代码段
//			pb->Func1();
//					mov         rax,qword ptr [pb]
//					mov         rax, qword ptr [rax]
//					mov         rcx, qword ptr [pb]
//					call        qword ptr [rax]
//00007FF681C011D1	jmp         C::Func1 (07FF681C06AC0h)		//进行了封装,需要先跳转至中间语句
//00007FF681C06AC0	sub         rcx,10h							//rcx中存储的是c中B的地址,然Func1的跳转地址在A的__vfptr中存储
//																//需要再偏移地址,减去0x10个字节
//00007FF681C06AC4	jmp         C::Func1 (07FF681C01069h)		//做完中间步骤,再去跳转至统一跳转Func1地址的地址
//00007FF681C01069	jmp         C::Func1 (07FF681C06A60h)
//
//class A
//{
//public:
//	virtual void Func1()
//	{
//		cout << "A::Func1()" << endl;
//	}
//	virtual void Func2()
//	{
//		cout << "A::Func2()" << endl;
//	}
//	virtual void Func3()
//	{
//		cout << "A::Func3()" << endl;
//	}
//	virtual void Func4()
//	{
//		cout << "A::Func4()" << endl;
//	}
//private:
//	int _a = 1;
//};
//class B : virtual public A
//{
//public:
//	virtual void Func1()
//	{
//		cout << "B::Func1()" << endl;
//	}
//	virtual void Func2()
//	{
//		cout << "B::Func2()" << endl;
//	}
//private:
//	int _b = 2;
//};
//class C : virtual public A
//{
//public:
//	virtual void Func1()
//	{
//		cout << "C::Func1()" << endl;
//	}
//	virtual void Func3()
//	{
//		cout << "C::Func3()" << endl;
//	}
//private:
//	int _c = 3;
//};
//class D : public B, public C
//{
//public:
//	virtual void Func1()
//	{
//		cout << "D::Func1()" << endl;
//	}
//	virtual void Func4()
//	{
//		cout << "D::Func4()" << endl;
//	}
//private:
//	int _d = 4;
//};
//int main()
//{
//	D d;
//
//	d.Func2();
//	d.Func3();
//	d.C::Func1();
//
//	return 0;
//}
//class Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Base::Func1()" << endl;
//	}
//	virtual void Func2()
//	{
//		cout << "Base::Func2()" << endl;
//	}
//private:
//	int _b = 1;
//};
//class Derive1 : virtual public Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Derive1::Func1()" << endl;
//	}
//	virtual void Test()
//	{
//		cout << "Derive1::Test()" << endl;
//	}
//private:
//	int _d = 2;
//};
//class Derive2 : virtual public Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Derive2:Func1()" << endl;
//	}
//	virtual void Test()
//	{
//		cout << "Derive2::Test()" << endl;
//	}
//private:
//	int _d = 3;
//};
//class AAA : public Derive1, public Derive2
//{
//public:
//	virtual void Func1()
//	{
//		cout << "AAA::Func1()" << endl;
//	}
//	virtual void Func3()
//	{
//		cout << "AAA::Func2()" << endl;
//	}
//	virtual void Test()
//	{
//		cout << "AAA::Test()" << endl;
//	}
//private:
//	int _a = 4;
//};
//int main()
//{
//	AAA a;
//	
//	return 0;
//}
//void Test(Base& x)
//{
//	x.Func1();
//	x.Func2();
//	x.Func3();
//}
//typedef void* VFPTR;
//void PrintVFTable(VFPTR* vftable)
//{
//	cout << "虚表地址:" << vftable << endl;
//	for (size_t i = 0; vftable[i] != nullptr; ++i)
//	{
//		printf("\t第%d个虚函数地址 :0X%p,->", i, vftable[i]);
//		((void(*)())vftable[i])();
//	}
//	cout << endl;
//}
//void ChangeVirtualTable(VFPTR* vftable, void* dest)
//{
//	//虚表存放在代码段中,使用该方法修改程序运行时会崩溃
//	*vftable = (VFPTR)dest;
//}
//int main()
//{
//	Base b;
//	cout << sizeof(b) << endl;
//	Test(b);
//	b.Func3();
//
//	Derive d;
//	cout << sizeof(d) << endl;
//	Test(d);
//
//	AAA a;
//	cout << sizeof(a) << endl;
//	Test(a);
//	a.Base::Func1();
//	cout << endl;
//
//	Derive* pa = &a;
//	pa->Test();
//	d.Test();
//
//	BBB* pb = &a;
//	pb->Func1();
//	pb->Func4();
//	pb->Func666();
//
//	CCC* pc = &a;
//	pc->Func666();
//	//ChangeVirtualTable((VFPTR*)(*((VFPTR*)&d)), main);
//
//	VFPTR* ptr = (VFPTR*)(*(VFPTR*)&d);
//	void (*func)(void) = (void(*)(void))ptr[0];
//	func();
//
//	//先取对象的地址,强转为8Byte的类型指针后,再解引用(指针类型是8Byte,只有强转为8Byte类型的指针后,解引用才能将指针取出)
//	PrintVFTable((VFPTR*)(*((long long*)(&b))));
//	PrintVFTable((VFPTR*)(*((long long*)(&d))));
//	PrintVFTable((VFPTR*)(*((long long*)(&a))));
//	PrintVFTable((VFPTR*)(*((long long*)(pa))));
//
//	return 0;
//}
//class A
//{
//public:
//	void Func1()
//	{
//		cout << "A::Func1()" << endl;
//	}
//private:
//	int _a = 0;
//};
//class B : virtual public A
//{
//private:
//	int _b = 1;
//};
//class C : virtual public A
//{
//private:
//	int _c = 2;
//};
//class D : public B, public C
//{
//private:
//	int _d = 3;
//};
//int main()
//{
//	D d;
//	d.A::Func1();
//	return 0;
//}
//多态test
//class A
//{
//public:
//	virtual void Func1()
//	{
//		cout << "A::Func1" << endl;
//	}
//private:
//	int _a = 1;
//};
//class B
//{
//public:
//	virtual void Func2()
//	{
//		cout << "B::Func2" << endl;
//	}
//private:
//	int _b = 2;
//};
//class C : public A, public B
//{
//public:
//	virtual void Func2()
//	{
//		cout << "C::Func2" << endl;
//	}
//private:
//	int _c = 3;
//};
//int main()
//{
//	B* pb = new C;
//	pb->Func2();
//	return 0;
//}

//二叉树进阶
//map和set特性需要先铺垫二叉搜索树(一种树形结构)
//二叉搜索树	BST		Binary Search Tree
//		二叉排序树/二叉查找树
//它或者是一棵空树,或者是具有以下性质的二叉树:
//		若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
//		若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
//		它的左右子树也分别为二叉搜索树
//默认的搜索树是不允许数值冗余的,即键值不同
//二叉搜索树的查找
//		从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
//		最多查找高度次,走到到空,还没找到,这个值不存在
//二叉搜索树的插入
//		树为空,则直接新增节点,赋值给root指针
//		树不空,按二叉搜索树性质查找插入位置,插入新节点
//二叉搜索树的删除
//首先查找元素是否在二叉搜索树中,如果不存在,则返回
//		该结点无孩子结点:	直接删除
//		只有左孩子结点:		删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
//		只有右孩子结点:		删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
//		有左,右孩子结点:	在它的右子树中寻找中序下的第一个结点(值最小),用它的值填补到被删除节点中,再来处理该结点的删除问题(替换法删除)
//
//BinarySerachTree实现测试
#include "BinarySrearchTree.h"
namespace qyc
{
	namespace test
	{
		void KTree_Insert()
		{
			using namespace key;

			cout << "KTree_Insert()" << endl;
			
			int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13, 8, 3, 1, 10, 6, 4, 7, 14, 13 };
			BinarySearchTree<int> bst;
			
			for (auto e : a)
				cout << bst.Insert(e) << ' ';
			cout << endl;
			
			bst.InOrder();
			cout << endl;
			
			BinarySearchTree<int> cpy = bst;
			cout << cpy.size() << endl;
			cpy.InOrder();
			cout << endl;

			BinarySearchTree<int> _cpy(a, a + 5);
			_cpy.InOrder();
			cout << endl;
			
			_cpy = cpy;
			_cpy.InOrder();
			cout << endl;

			BinarySearchTree<string> bsts;
			vector<string> v1 = { "insert", "erase", "find", "size", "empty", "sort" };
			for (const auto& e : v1)
				bsts.insert(e);
			bsts.InOrder();
			cout << endl;
			cout << bsts.find("insert") << endl;

			cout << endl;
		}

		void KVTree_Insert()
		{
			using namespace key_value;

			cout << "KVTree_Insert()" << endl;

			vector<string> v1 = { "insert", "erase", "find", "size", "empty", "sort", "push", "pop", "remove" };
			vector<string> v2 = { "插入", "去除", "查找", "大小", "判空", "排序", "压入", "弹出", "删除" };

			BinarySearchTree<string, string> bst;

			for (size_t i = 0; i < v1.size(); ++i)
				bst.insert(v1[i], v2[i]);
			bst.InOrder();
			cout << endl;

			TreeNode<string, string>* pfind = bst.find("erase");
			if (pfind != nullptr)
				pfind->_value = "擦除";
			bst.InOrder();
			cout << endl;
			
			pfind = bst.find("_erase");
			if (pfind == nullptr)
				cout << "pfind == nullptr" << endl;
			
			pfind = bst.find("pop");
			stack<TreeNode<string, string>*> st = bst.find_path(pfind);
			while (!st.empty())
			{
				cout << "/" << st.top()->_key;
				st.pop();
			}
			cout << endl;

			vector<string> v3 = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			BinarySearchTree<string, int> bst1;
			for (const auto& e : v3)
			{
				if (bst1.insert(e, 1) == false)
				{
					auto pf = bst1.find(e);
					++pf->_value;
				}
			}
			bst1.InOrder();
			cout << endl;

			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		qyc::test::KTree_Insert();
//		qyc::test::KVTree_Insert();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	return 0;
//}
////前序中序构建二叉树
//struct TreeNode {
//	int val;
//	TreeNode* left;
//	TreeNode* right;
//	TreeNode() : val(0), left(nullptr), right(nullptr) {}
//	TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
//	TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
//};
//TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//	if (inorder.size() == 0)
//		return nullptr;
//	TreeNode* root = new TreeNode(preorder[0]);
//
//	vector<int>::iterator findex = find(inorder.begin(), inorder.end(), preorder[0]);
//	if (findex != inorder.end())
//	{
//		preorder.erase(preorder.begin());
//
//		vector<int> leftinorder(inorder.begin(), findex);
//		root->left = buildTree(preorder, leftinorder);
//
//		vector<int> rightinorder(findex + 1, inorder.end());
//		root->right = buildTree(preorder, rightinorder);
//	}
//
//	return root;
//}
////中序后序构建二叉树
//TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
//	if (inorder.size() == 0)
//		return nullptr;
//
//	TreeNode* root = new TreeNode(postorder.back());
//	vector<int>::iterator findex = find(inorder.begin(), inorder.end(), root->val);
//	if (findex != inorder.end())
//	{
//		postorder.pop_back();
//		vector<int> rightinorder(findex + 1, inorder.end());
//		root->right = buildTree(rightinorder, postorder);
//
//		vector<int> leftinorder(inorder.begin(), findex);
//		root->left = buildTree(leftinorder, postorder);
//
//	}
//	return root;
//}

#include <set>
#include <map>
//map & set
//关联式容器
//STL中的部分容器 vector list deque forward_list等,统称为序列式容器
//		其底层为线性序列的数据结构,里面存储的是元素本身
//关联式容器也是用来存储数据的
//		其里面存储的是<key, value>结构的键值对
//在数据检索时比序列式容器效率更高
//
//键值对
//用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value
//		key代表键值,value表示与key对应的信息
//SGI-STL中关于键值对的定义:
//template <class T1, class T2>
//struct pair
//{
//	typedef T1 first_type;
//	typedef T2 second_type;
//	T1 first;
//	T2 second;
//	pair() : first(T1()), second(T2())
//	{}
//	pair(const T1& a, const T2& b) : first(a), second(b)
//	{}
//};
//
//树形结构的关联式容器
//根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:		树型结构与哈希结构
//树型结构的关联式容器主要有四种:	map set multimap multiset
//这四种容器的共同点是:
//		使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列
//
//set
//set是按照一定次序存储元素的容器
//在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的
//set中的元素不能在容器中修改(元素为const),但是可以从容器中插入或删除它们
//在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序
//set容器通过key访问单个元素的速度通常比unordered_set容器慢,但允许根据顺序对子集进行直接迭代
//注:
//	与map/multimap不同,map/multimap中存储的是键值对<key, value>		set中只放value,底层存放的是由<value, value>构成的键值对
//	插入元素时,只需要插入value即可,不需要构造键值对
//	元素不可以重复,可以用set去重
//	使用迭代器遍历set中的元素,可以得到有序序列
//	元素默认按照小于(less)来比较
//	查找某个元素的时间复杂度: logN
//	元素不允许修改
//	底层使用二叉搜索树(红黑树)来实现
//multiset
//接口操作与set完全相同
//其允许存储多个相同数据
//find函数查找存在多个值时,返回中序遍历的第一个值
//
//set模板实现
//template<class T, class Compare = less<T>, class Alloc = allocator<T>> class set;
//T:		set中存放元素的类型,实际在底层存储<value, value>的键值对
//Compare:	set中元素默认按照小于来比较
//Alloc:	set中元素空间的管理方式,使用STL提供的空间配置器管理
//
//map
//关联容器	按照特定的次序(按key比较)存储由键值key和值value组合而成的元素
//在map中,键值key通常用于排序和唯一的标识元素,而值value中存储与此键值key关联的内容
//	键值key和值value的类型可能不同
//	在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
//			typedef pair<const key, T> value_type;
//在内部,map中的元素总是按照键值key进行比较排序的
//map中通过键值访问单个元素的速度通常比unordered_map容器慢
//	map允许根据顺序对元素进行直接迭代(对map中的元素进行迭代,可得有序序列)
//map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
//map通常被实现为平衡二叉搜索树(红黑树)
//
//map实现模板
//template <class key, class T, class Compare = less<key>, class Alloc = allocator<pair<const key, T>>> class map;
//key:	键值对中key的类型
//T:	键值对中value的类型
//Compare: 比较器的类型,map中的元素是按照key来比较的
//			缺省情况下按照小于来比较,一般情况下该参数不需要传递,无法比较时(自定义类型),需用户自己显式传递
//Alloc:	通过空间配置器来申请底层空间，不需要用户传递，除非用户不想使用标准库提供的空间配置器
//
//注:
//	在元素访问时,operator[] at函数,都可以通过key找到对应value返回其引用
//	当key不存在时	operator[]用默认value与key构造键值对然后插入,返回该默认value
//					at()函数直接抛异常
//multimap
//接口操作与map完全相同		multimap无operator[]
//其允许存储多个相同数据
//find函数查找存在多个值时,返回中序遍历的第一个值
//void Set_Set()
//{
//	cout << "Set_Set" << endl;
//
//	set<int> s;
//	int a[] = { 3, 2, 4, 5, 1, 3, 2, 4, 5, 1 };
//	s = set<int>(a, a + 5);
//	set<int>::iterator it = s.begin();
//	while (it != s.end())
//		cout << *it++ << ' ';
//	cout << endl;
//
//	for (const auto& e : s)
//		cout << e << ' ';
//	cout << endl;
//
//	s.erase(3);
//	for (const auto& e : s)
//		cout << e << ' ';
//	cout << endl;
//
//	s.insert(3);
//	set<int>::iterator pos = s.find(3);		//查找效率为O(logN)
//	//set<int>::iterator pos = find(s.begin(), s.end(), 3);		//查找效率为O(N)
//	s.erase(pos);
//	for (const auto& e : s)
//		cout << e << ' ';
//	cout << endl;
//
//	s.clear();
//	for (int i = 1; i < 10; ++i)
//		s.insert(i * 10);
//	for (const auto& e : s)
//		cout << e << ' ';
//	cout << endl;
//	set<int>::iterator low = s.lower_bound(30);		//查找>=key的第一个元素
//	set<int>::iterator up = s.upper_bound(60);		//查找>key的第一个元素
//	cout << *low << ' ' << *up << endl;
//	s.erase(low, up);
//	for (const auto& e : s)
//		cout << e << ' ';
//	cout << endl;
//
//	cout << endl;
//}
//void Set_Multiset()
//{
//	cout << "Set_Multiset" << endl;
//
//	multiset<int> ms;
//	for (int i = 0; i < 10; ++i)
//		ms.insert(i);
//	ms.insert(5);
//	ms.insert(5);
//	ms.insert(5);
//	ms.insert(5);
//	for (const auto& e : ms)
//		cout << e << ' ';
//	cout << endl;
//	cout << ms.count(5) << endl;
//	multiset<int>::iterator findex = ms.find(5);
//	while (findex != ms.end())
//		cout << *findex++ << ' ';
//	cout << endl;
//
//	pair<multiset<int>::iterator, multiset<int>::iterator> ret = ms.equal_range(5);
//	cout << *ret.first << ' ' << *ret.second << endl;
//	ms.erase(ret.first, ret.second);
//	for (const auto& e : ms)
//		cout << e << ' ';
//	cout << endl;
//
//	cout << endl;
//}
//void Map_Map()
//{
//	cout << "Map_Map" << endl;
//
//	map<string, string> dict;
//
//	pair<string, string> kv1("insert", "插入");
//	dict.insert(kv1);
//	dict.insert(pair<string, string>("erase", "删除"));
//	dict.insert(make_pair("sort", "排序"));
//	//C++11支持多参数的构造函数隐式类型转换
//	//C++98仅支持单参数隐式类型转换
//	dict.insert({ "find", "查找" });
//
//	map<string, string>::iterator it = dict.begin();
//	while (it != dict.end())
//	{
//		cout << it->first << ':' << it->second << ' ';
//		++it;
//	}
//	cout << endl;
//
//	for (const auto& kv : dict)
//		cout << kv.first << ':' << kv.second << ' ';
//	cout << endl;
//
//	vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
//	map<string, int> box;
//	for (const auto& e : vs)
//		++box[e];
//	for (const auto& e : box)
//		cout << e.first << ':' << e.second << ' ';
//	cout << endl;
//
//	cout << endl;
//}
//int main()
//{
//	Set_Set();
//	Set_Multiset();
//	Map_Map();
//	return 0;
//}
//AVLTree
//二叉搜索树虽可以缩短查找的效率,若数据有序或接近有序二叉搜索树将退化为单支树,时间复杂度变成O(N)
//		查找元素相当于在顺序表中搜索元素,效率低下
//		因此map,set等关联式容器的底层结构是对二叉树进行了平衡处理
//对二叉树平衡的解决方法:
//	当向二叉搜索树中插入新结点后,若能保证每个结点的左右子树高度之差的绝对值不超过1,即可降低树的高度,减少平均搜索长度
//一棵AVL树具有以下性质:
//		左右子树高度之差(平衡因子)的绝对值不超过1(-1/0/1)
//		左右子树都是AVL树
//如果一棵二叉搜索树是高度平衡的,就是AVL树
//		有n个结点,其高度可保持在logN,搜索时间复杂度O(logN)
//
//AVL树的插入
//AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树
//AVL树的插入过程分为两步:
//		按照二叉搜索树的方式插入新节点
//		调整节点的平衡因子
//
//先按照二叉搜索树的规则将节点插入到AVL树中
//新节点插入后,AVL树的平衡性可能会遭到破坏,更新平衡因子,并检测是否破坏了AVL树的平衡性
//	cur插入后,pParent的平衡因子一定需要调整
//在插入之前，parent的平衡因子分为三种情况: -1,0,1
//插入后分以下两种情况:
//		如果cur插入到parent的左侧,给parent的平衡因子-1
//		如果cur插入到parent的右侧,给parent的平衡因子+1
//parent的平衡因子可能有三种情况: 0, 1/-1, 2/-1
//若parent的平衡因子为0,插入之前parent的平衡因子为1/-1,插入后被调整成0,满足AVL树的性质,插入成功
//若parent的平衡因子为1/-1,说明插入前parent的平衡因子一定为0,插入后被更新成正负1,此时以parent为根的树的高度增加,需向上更新
//若parent的平衡因子为2/-2,则parent的平衡因子违反平衡树的性质,需要对其进行旋转处理
//
//AVLTree的旋转
//根据插入位置的不同,AVLTree旋转分为4种
//	新节点插入较高左子树的左侧:	左左	右单旋
//		右单旋后,原根的左子树与原根交换位置
//			原根左子树的右子树变为原根的左子树
//			原根左子树和原根的平衡因子都变为0
//	新节点插入较高右子树的右侧:	右右	左单旋
//		左单旋后,原根的右子树与原根交换位置
//			原根右子树的左子树变为原根的右子树
//			原根右子树和原根的平衡因子都变为0
//	新节点插入较高左子树的右侧:	左右	左单旋后右单旋	左右双旋
//		先对原根的左子树进行左单旋
//			使原根的左子树与原根左子树的右子树交换位置
//		在对原根进行右单旋
//			使原根左子树的右子树与原根交换位置
//		当新插入节点在较高左子树右根的左侧	原根的平衡因子为1,原根左子树的平衡因子和原根左子树的右子树平衡因子为0
//									  右侧	原根左子树的平衡因子为-1,原根的平衡因子和原根左子树的右子树平衡因子为0
//	新节点插入较高右子树的左侧:	右左	右单旋后左单旋	右左双旋
//		先对原根的右子树进行右单旋
//			使原根的右子树与原根右子树的左子树交换位置
//		在对原根进行左单旋
//			使原根右子树的左子树与原根交换位置
//		当新插入节点在较高右子树左根的左侧	原根右子树的平衡因子为1,原根的平衡因子和原根右子树的左子树平衡因子为0
//									  右侧	原根的平衡因子为-1,原根右子树的平衡因子和原根右子树的左子树平衡因子为0
//当旋转完成后,原根子树的高度降低,已平衡,无需再向上调整
//删除旋转分6种情况
//
//总结:
//	子树不平衡时,即根的平衡因子为2/-2
//		 当cur与parent平衡因子符号相同时	单旋
//			当parent平衡因子>0时,左单旋
//			当parent平衡因子<0时,右单旋
//		 当cur与parent平衡因子符号不同时	双旋
//			当parent平衡因子>0时,右左双旋
//			当parent平衡因子<0时,左右双旋
//
//AVLTree性能
//AVL树是一棵绝对平衡的二叉搜索树,每个节点的左右子树高度差的绝对值都不超过1
//	保证查询时高效的时间复杂度,	O(logN)
//但要对AVL树做一些结构修改的操作,性能非常低下
//因此,需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树
//	但一个结构经常修改,就不太适合
//
//红黑树	一种二叉搜索树
//	在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black
//	通过对任何一条从根到叶子的路径上各个结点着色方式的限制,确保没有一条路径会比其他路径长出俩倍,是接近平衡的
//红黑树的性质
//		每个结点不是红色就是黑色
//		根节点是黑色的
//		如果一个节点是红色的,则它的两个孩子结点是黑色的	(树中没有连续的红色节点)
//		对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点	(每条路径上黑色节点数目相等)
//		每个叶子结点都是黑色的(叶子结点指空结点)	也成为NIL节点
//	根据以上性质得出:	极限最短的路径:	全为黑色节点
//						极限最长的路径:	一个黑色节点一个红色节点
//满足上面的性质,红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍
//在节点的定义中,将节点的默认颜色给成红色
//
//红黑树结构
//为实现关联式容器简单,红黑树的实现中增加一个头结点
//	因为根节点必须为黑色,为了与根节点进行区分,将头结点给成黑色
//	并让头结点的pParent域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点
//
//红黑树的插入操作
//红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
//		按照二叉搜索树的规则插入新节点
//		检测新节点插入后,红黑树的性质是否遭到破坏
//			若满足直接退出,否则对红黑树进行旋转着色处理
//			根节点的颜色可能被修改,需将其改回黑色
//判断红黑树的性质是否遭到破坏
//因为新节点的默认颜色是红色,因此,若其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整
//但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点
//需要对红黑树分情况来讨论:
//	
//
//测试实现AVLTree & RBTree
#include "utility.h"
#include "AVLTree.h"
#include "RBTree.h"
#include <Windows.h>
namespace qyc
{
	namespace test
	{
		template<class K>
		struct KeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
		template<class K, class V>
		struct KeyOfPair
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		void AVLTree_AVLTree()
		{
			cout << "AVLTree_AVLTree" << endl;

			std::vector<int> v = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
			//v = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
			//v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
			std::reverse(v.begin(), v.end());
			AVLTree<int, int, KeyOfT<int>, less<int>> tree(v.begin(), v.end());

			AVLTree<int, int, KeyOfT<int>, less<int>> cpy1 = tree;
			AVLTree<int, int, KeyOfT<int>> cpy2;
			cpy2 = tree;
			cout << ((cpy2 == cpy1) && (cpy1 == tree)) << endl;
			cout << "copy: " << cpy2.balance() << endl;
 			cout << "size: " << tree.size() <<  ' ' << v.size() << endl;
			cout << "hight: " << tree.hight() << endl;
			cout << "balance: " << tree.balance() << endl;

			vector<int> ret = tree.inorder();
			for (const auto& e : ret)
				cout << e << ' ';
			cout << endl;

			ret = tree.preorder();
			for (const auto& e : ret)
				cout << e << ' ';
			cout << endl;

			vector<vector<int>> lret = tree.levelorder();
			for (const auto& v : lret)
			{
				for (const auto& e : v)
					cout << e << ' ';
				cout << "| ";
			}
			cout << endl;
			cout << endl;

			for (const auto& e : v)
			{
				tree.erase(e);
				ret = tree.inorder();
				for (const auto& e : ret)
					cout << e << ' ';
				cout << endl;

				ret = tree.preorder();
				for (const auto& e : ret)
					cout << e << ' ';
				cout << endl;

				lret = tree.levelorder();
				for (const auto& v : lret)
				{
					for (const auto& e : v)
						cout << e << ' ';
					cout << "| ";
				}
				cout << endl;
				cout << "balance: " << tree.balance() << endl;
				cout << endl;
			}

			AVLTree<int, int, KeyOfT<int>> test;
			srand((unsigned int)time(nullptr));
			v.clear();
			for (int i = 0; i < 500; ++i)
			{
				v.push_back(rand());
				test.insert(v.back());
				if (test.balance() == false)
					cout << "break: " << v.back() << endl;
			}
			cout << endl;
			cout << v.size() << endl;
			for (const auto& e : v)
			{
				test.erase(e);
				if (test.balance() == false)
					cout << "break: " << e << endl;
			}
			cout << endl;

			cout << endl;
		}

		void RBTree_RBTree()
		{
			cout << "RBTree_RBTree" << endl;

			std::vector<int> v = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
			//v = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
			//v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
			std::reverse(v.begin(), v.end());
			RBTree<int, int, KeyOfT<int>> rbt(v.begin(), v.end());
			cout << rbt.IsValidRBTree() << endl;
			cout << "hight: " << rbt.hight() << endl;
			cout << "size: " << rbt.size() << endl;
			for (const auto& e : v)
				rbt.erase(e);

			RBTree<int, int, KeyOfT<int>> cpy = rbt;
			RBTree<int, int, KeyOfT<int>> _cpy;
			_cpy = cpy;
			cout << ((_cpy == cpy) && (rbt == cpy)) << endl;
			vector<int> ret = rbt.inorder();
			for (const auto& e : ret)
				cout << e << ' ';
			cout << endl;

			v.clear();
			rbt.clear();
			srand((unsigned int)time(nullptr));
			for (int i = 0; i < 5000; ++i)
				v.push_back(rand() % 10000);
			rbt = RBTree<int, int, KeyOfT<int>>(v.begin(), v.end());
			cout << rbt.IsValidRBTree() << endl;
			for (const auto& e : v)
			{
				rbt.erase(e);
				if (rbt.IsValidRBTree() == false)
					cout << "break: " << e << endl;
			}

			cout << endl;
		}

		void BST_AVLT_RBT()
		{
		#define NUM 100000000
			double begin = 0.0, tmp = 0.0;

			//cout << "BinarySearchTree_Test" << endl;
			//key_value::BinarySearchTree<int, int> bst;
			//
			//begin = (double)GetTickCount64();
			//for (int i = 0; i < NUM; ++i)
			//	bst.insert(i, i);
			//tmp = (double)GetTickCount64();
			//printf("insert over: %.5f s\n", ((tmp - begin) / 1000));
			//
			//begin = tmp;
			//for (int i = 0; i < NUM; ++i)
			//	bst.find(i);
			//tmp = (double)GetTickCount64();
			//printf("find over: %.5f s\n", ((tmp - begin) / 1000));
			//
			//begin = tmp;
			//for (int i = 0; i < NUM; ++i)
			//	bst.erase(i);
			//tmp = (double)GetTickCount64();
			//printf("erase over: %.5f s\n", ((tmp - begin) / 1000));

			//cout << endl;

			cout << "AVLTree_Test" << endl;
			AVLTree<int, int, KeyOfT<int>> avl;
			
			begin = (double)GetTickCount64();
			for (int i = 0; i < NUM; ++i)
				avl.insert(i);
			tmp = (double)GetTickCount64();
			printf("insert over: %.5f s\n", ((tmp - begin) / 1000));

			begin = tmp;
			for (int i = 0; i < NUM; ++i)
				avl.find(i);
			tmp = (double)GetTickCount64();
			printf("find over: %.5f s\n", ((tmp - begin) / 1000));
			
			begin = tmp;
			for (int i = 0; i < NUM; ++i)
				avl.erase(i);
			tmp = (double)GetTickCount64();
			printf("erase over: %.5f s\n", ((tmp - begin) / 1000));

			cout << endl;

			cout << "RBTree_Test" << endl;
			RBTree<int, int, KeyOfT<int>> rbt;

			begin = (double)GetTickCount64();
			for (int i = 0; i < NUM; ++i)
				rbt.insert(i);
			tmp = (double)GetTickCount64();
			printf("insert over: %.5f s\n", ((tmp - begin) / 1000));

			begin = tmp;
			for (int i = 0; i < NUM; ++i)
				rbt.find(i);
			tmp = (double)GetTickCount64();
			printf("find over: %.5f s\n", ((tmp - begin) / 1000));

			begin = tmp;
			for (int i = 0; i < NUM; ++i)
				rbt.erase(i);
			tmp = (double)GetTickCount64();
			printf("erase over: %.5f s\n", ((tmp - begin) / 1000));
		#undef NUM
		}
	}
}
//int main()
//{
//	try
//	{
//		//qyc::test::AVLTree_AVLTree();
//		qyc::test::RBTree_RBTree();
//		//qyc::test::BST_AVLT_RBT();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unknow exception" << endl;
//	}
//	return 0;
//}
//
#include "set.h"
#include "map.h"
//测试实现map & set
namespace qyc
{
	namespace test
	{
		void RedBlackTree_Iterator()
		{
			cout << "RedBlackTree_Iterator" << endl;
			
			vector<int> v = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
			RBTree<int, int, KeyOfT<int>> rbt(v.begin(), v.end());
			for (const auto& e : v)
				cout << e << ' ';
			cout << endl;
			v = rbt.inorder();
			for (const auto& e : v)
				cout << e << ' ';
			cout << endl;
			for (const auto& e : v)
			{
				if (rbt.IsValidRBTree() == false)
					cout << "Break: " << e << endl;
				rbt.erase(e);
			}
			cout << endl;

			v.clear();
			rbt.clear();
			srand((unsigned int)time(nullptr));
			for (int i = 0; i < 5000; ++i)
				v.push_back(rand() % 10000);
			for (const auto& e : v)
			{
				rbt.insert(e);
				if (rbt.IsValidRBTree() == false)
					cout << "break: " << e << endl;
			}
			for (const auto& e : v)
			{
				rbt.erase(e);
				if (rbt.IsValidRBTree() == false)
					cout << "break: " << e << endl;
			}

			RBTree<int, pair<int, int>, KeyOfPair<int, int>> rbt1;
			vector<pair<int, int>> v1;
			for (int i = 0; i < 5000; ++i)
				v1.push_back({ rand() % 10000, 0 });
			for (const auto& e : v1)
			{
				rbt1.insert(e);
				if (rbt1.IsValidRBTree() == false)
					cout << "Break: " << e.first << endl;
			}
			for (const auto& e : v1)
			{
				rbt1.erase(e.first);
				if (rbt1.IsValidRBTree() == false)
					cout << "Break: " << e.first << endl;
			}

			v1.clear();
			for (int i = 1; i <= 20; ++i)
				v1.push_back({ i, i });
			for (const auto& e : v1)
			{
				rbt1.insert(e);
				if (rbt1.IsValidRBTree() == false)
					cout << "Break: " << e.first << endl;
			}
			for (const auto& e : rbt1)
				cout << e.first << ':' << e.second << ' ';
			cout << endl;
			auto rit = rbt1.rbegin();
			while (rit != rbt1.rend())
			{
				cout << rit->first << ':' << rit->second << ' ';
				++rit;
			}
			cout << endl;
			
			cout << endl;
		}
		
		void Map_Set()
		{
			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			map<string, int> box;
			for (const auto& e : vs)
				++box[e];
			for (const auto& e : box)
				cout << e.first << ':' << e.second << ' ';
			cout << endl;

			set<int, greater<int>> s;
			int a[] = { 3, 2, 4, 5, 1, 3, 2, 4, 5, 1 };
			s = set<int, greater<int>>(a, a + 5);
			set<int, greater<int>>::iterator it = s.begin();
			while (it != s.end())
				cout << *it++ << ' ';
			cout << endl;
			
			for (const auto& e : s)
				cout << e << ' ';
			cout << endl;
			
			s.erase(3);
			for (const auto& e : s)
				cout << e << ' ';
			cout << endl;
		}
		void multiMap_multiSet()
		{
			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			multimap<string, int> box;
			for (const auto& e : vs)
				box.insert({e, 1});
			for (const auto& e : box)
				cout << e.first << ':' << e.second << ' ';
			cout << endl;

			multiset<int, greater<int>> s;
			int a[] = { 3, 2, 4, 5, 1, 3, 2, 4, 5, 1 };
			s = multiset<int, greater<int>>(a, a + 10);
			multiset<int, greater<int>>::iterator it = s.begin();
			while (it != s.end())
				cout << *it++ << ' ';
			cout << endl;

			for (const auto& e : s)
				cout << e << ' ';
			cout << endl;

			s.erase(3);
			for (const auto& e : s)
				cout << e << ' ';
			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		//qyc::test::AVLTree_AVLTree();
//		//qyc::test::RedBlackTree_Iterator();
//		qyc::test::Map_Set();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unknow exception" << endl;
//	}
//	return 0;
//}
//
//计算两个数组之间的交集,差集
//vector<int> intersection(vector<int>& num1, vector<int>& num2)
//{
//	set<int> s1(num1.begin(), num1.end());
//	set<int> s2(num2.begin(), num2.end());
//
//	vector<int> ret;
//	set<int>::iterator it1 = s1.begin();
//	set<int>::iterator it2 = s2.begin();
//	while (it1 != s1.end() && it2 != s2.end())
//	{
//		if (*it1 < *it2)
//			++it1;
//		else if (*it1 > *it2)
//			++it2;
//		else
//		{
//			ret.push_back(*it1);
//			++it1;
//			++it2;
//		}
//	}
//	return ret;
//}
//vector<int> differencesetion(vector<int>& num1, vector<int>& num2)
//{
//	set<int> s1(num1.begin(), num1.end());
//	set<int> s2(num2.begin(), num2.end());
//
//	vector<int> ret;
//	set<int>::iterator it1 = s1.begin();
//	set<int>::iterator it2 = s2.begin();
//	while (it1 != s1.end() || it2 != s2.end())
//	{
//		if (it1 == s1.end() || *it1 < *it2)
//		{
//			ret.push_back(*it1);
//			++it1;
//		}
//		else if (it2 == s2.end() || *it1 > *it2)
//		{
//			ret.push_back(*it2);
//			++it2;
//		}
//		else
//		{
//			++it1;
//			++it2;
//		}
//	}
//	return ret;
//}

#include "unordered_set"
#include "unordered_map"
//哈希
// unordered系列关联式容器
//C++98,STL提供了底层为红黑树结构的关联式容器,查询效率为O(logN),即最差情况下需要比较红黑树的高度次
//		当树中的节点非常多时,查询效率也不理想,最好的查询是,进行很少的比较次数就能够将元素找到
//因此C++11,STL又提供了4个unordered系列的关联式容器 unordered_set/unordered_multiset/unordered_map/unordered_multimap
//	这四个容器与红黑树结构的关联式容器使用方式基本类似，只是其底层结构不同
//
//unordered_map
//	1. 是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value
//	2. 键值通常用于唯一的标识元素,而映射值是一个对象,其内容与此键关联	键和映射值的类型可能不同
//	3. 在内部没有对<kye, value>按照任何特定的顺序排序,为了能在常数范围内找到key所对应的value,将相同哈希值的键值对放在相同的桶中
//	4. 通过key访问单个元素要比较快,但在遍历元素子集的范围迭代方面效率较低
//	5. 实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value
//	6. 迭代器至少是前向迭代器
//unordered系列支持桶操作
//size_t bucket_count()const		返回哈希桶中桶的总个数
//size_t bucket_size(size_t n)const	返回n号桶中有效元素的总个数
//size_t bucket(const K& key)		返回元素key所在的桶号
//模板实现
//template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<pair<const Key, T>>> class unordered_map;
//unordered_set
//	使用方式与set基本相同	性质特点与unorder_map基本相同
//template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<Key>> class unordered_set;
//底层结构
//	unordered系列的关联式容器之所以效率比较高,其底层是使用了哈希结构
//
//哈希概念
//顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,在查找一个元素时,要经过关键码的多次比较
//		顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN)	搜索的效率取决于搜索过程中元素的比较次数
//哈希结构
//	可以不经过任何比较，一次直接从表中得到要搜索的元素
//构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立映射关系,在查找时通过该函数可以很快找到该元素
//插入元素
//	根据待插入元素的关键码，以此函数计算出该元素的存储位置并按此位置进行存放
//搜索元素
//	对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
//该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)
//
//哈希冲突
//对于两个数据元素的关键字 <k, i>和 <k, j>(i != j)
//	有<k, i> != <k, j>, Hash(<k, i>) == Hash(<k, j>)
//即:	不同关键字通过相同哈希哈数计算出相同的哈希地址,称为哈希冲突/哈希碰撞
//把具有不同关键码而具有相同哈希地址的数据元素称为同义词
//
//哈希函数
//引起哈希冲突的一个原因可能是	哈希函数设计不够合理
//哈希函数设计原则:
//	哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m - 1之间
//	哈希函数计算出来的地址能均匀分布在整个空间中
//	哈希函数应该比较简单
//
//常见哈希函数
//	直接定址法
//		取关键字的某个线性函数为散列地址: Hash(Key) = A * Key + B
//		优点:	简单,均匀
//		缺点:	需要事先知道关键字的分布情况
//		使用场景:	适合查找比较小且连续的情况
//	除留余数法
//		设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数
//		按照哈希函数:	Hash(key) = key % p (p <= m),将关键码转换成哈希地址
//	平方取中法
//		对关键字进行平方,取平方后所得值的中间n为位置哈希地址
//		适合场景:	不知道关键字的分布,而位数又不是很大的情况
//	折叠法
//		将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些)
//		然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址
//		适合场景:	事先不需要知道关键字的分布,关键字位数比较多的情况
//	随机数法
//		选择一个随机函数,取关键字的随机函数值为它的哈希地址
//			Hash(key) = random(key)
//		适合场景:	关键字长度不等时采用此法
//	数学分析法
//		设有n个d位数，每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定相同
//		可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址
//		适合场景:	处理关键字位数比较大的,事先知道关键字的分布且关键字的若干位分布较均匀的情况
//哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但无法避免哈希冲突
//
//哈希冲突解决
//解决哈希冲突两种常见的方法是:		闭散列/开散列
//
//闭散列	开放定址法
//	当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置
//	那么可以把key存放到冲突位置中的下一个空位置中去
//线性探测:	从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止
//插入
//	通过哈希函数获取待插入元素在哈希表中的位置
//	如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素
//删除
//	采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索
//	因此线性探测采用标记的伪删除法来删除一个元素
//		哈希表每个空间给个标记
//		EMPTY此位置空， EXIST此位置已经有元素， DELETE元素已经删除
//		enum State { EMPTY, EXIST, DELETE };
//扩容
//	散列表的荷载因子定义为:		a = 填入表中元素个数 / 散列表长度
//	a 是散列表装满程序得到标志因子	表长是定值,a与填入表中元素个数成正比
//	a 越大,产生冲突的可能性越大,反之,产生冲突的可能性越小
//对于开放定址法,荷载因子是特别重要的因素
//	应严格限制在0.7 - 0.8以下,超过0.8,查表时CPU缓存不命中(cacha missing)按指数曲线上升
//线性探测优点:	实现非常简单
//线性探测缺点:	一旦发生哈希冲突,所有的冲突连在一起,容易产生数据"堆积"
//	即:	不同关键码占据了可利用的空位置,使得寻找某关键码的位置需要许多次比较,导致搜索效率降低
//二次探测
//线性探测的缺陷是产生冲突的数据堆积在一块
//这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找
//因此二次探测为了避免该问题
//	找下一个空位置的方法为:	H_i = (index + i^2) % m / (index - i^2) % m		其中: i = 1, 2, 3,......
//		index是通过散列函数Hash(x)对元素的关键码key进行计算得到的位置, m是表的大小
//研究表明:
//	当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次
//		因此只要表中有一半的空位置,就不会存在表满的问题
//	在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容
//因此:	闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷
//
//开散列		开散列法又叫链地址法(开链法)
//首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合
//	每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中
//		开散列中每个桶中放的都是发生哈希冲突的元素
//开散列增容
//桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多
//	极端情况下,可能会导致一个桶中链表节点非常多,会影响的哈希表的性能
//	因此在一定条件下需要对哈希表进行增容
//开散列最好的情况是:
//	每个哈希桶中刚好挂一个节点,再继续插入元素时,每一次都会发生哈希冲突
//		因此在元素个数刚好等于桶的个数时,可以给哈希表增容
//开散列与闭散列比较
//应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销
//事实上,由于开地址法必须保持大量的空闲空间以确保搜索效率
//		如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间
#include "HashTable.h"
#include "unordered_map.h"
#include "unordered_set.h"
namespace qyc
{
	namespace test
	{
		void CloseHashTable_Test()
		{
			cout << "CloseHashTable_Test" << endl;
			using namespace close_hash;
			vector<int> v = { 1, 11, 4, 15, 26, 7, 44, 9 };
			HashTable<int, int, KeyOfT<int>> ht;
			for (const auto& e : v)
				ht.insert(e);
			HashTable<int, int, KeyOfT<int>> _ht(ht);
			_ht.erase(4);
			cout << _ht.find(44) << endl;
			cout << _ht.find(4) << endl;
			cout << endl;

			v.clear();
			ht.clear();
			srand((unsigned int)time(nullptr));
			for (int i = 0; i < 100000; ++i)
			{
				v.push_back(rand());
				ht.insert(v.back());
			}
			cout << "insert over" << endl;
			for (const auto& e : v)
			{
				HashData<int>* ptr = ht.find(e);
				if (ptr)
					ht.erase(e);
			}
			cout << "fine & erase over" << endl;

			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			HashTable<string, pair<string, int>, KeyOfPair<string, int>> ht1;
			for (const auto& e : vs)
			{
				HashData<pair<string, int>>* retfind = ht1.find(e);
				if (retfind)
					++retfind->_data.second;
				else
					ht1.insert(make_pair(e, 1));
			}
			HashTable<string, pair<string, int>, KeyOfPair<string, int>> ht2(ht1);
			for (const auto& e : vs)
			{
				HashData<pair<string, int>>* retfind = ht2.find(e);
				if (retfind)
				{
					cout << retfind->_data.first << ':' << retfind->_data.second << ' ';
					ht2.erase(e);
				}
			}
			cout << endl;
			cout << endl;
		}

		void HashBucket_Test()
		{
			cout << "HashBucket_Test" << endl;

			using namespace hash_bucket;

			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			HashTable<string, pair<string, int>, KeyOfPair<string, int>> ht1;
			for (const auto& e : vs)
			{
				HashTable<string, pair<string, int>, KeyOfPair<string, int>>::iterator retfind = ht1.find(e);
				if (retfind != ht1.end())
					++retfind->second;
				else
					ht1.insert(make_pair(e, 1));
			}
			HashTable<string, pair<string, int>, KeyOfPair<string, int>> ht2(ht1);
			for (const auto& e : vs)
			{
				HashTable<string, pair<string, int>, KeyOfPair<string, int>>::iterator retfind = ht2.find(e);
				if (retfind != ht2.end())
				{
					cout << retfind->first << ':' << retfind->second << ' ';
					ht2.erase(e);
				}
			}
			cout << endl;

			vector<int> v = { 1, 11, 4, 15, 26, 7, 44, 9, 34, 54 };
			HashTable<int, int, KeyOfT<int>> ht;
			for (const auto& e : v)
				ht.insert(e);
			HashTable<int, int, KeyOfT<int>> _ht(ht);
			cout << *_ht.find(44) << endl;
			cout << *_ht.find(4) << endl;
			cout << _ht.bucket_size(1);
			_ht.erase(4);
			_ht.erase(44);
			if (_ht.find(44) == _ht.end())
				cout << "not found" << endl;
			if (_ht.find(4) == _ht.end())
				cout << "not found" << endl;
			cout << endl;

			v.clear();
			ht.clear();
			srand((unsigned int)time(nullptr));
			for (int i = 0; i < 100000; ++i)
			{
				v.push_back(rand());
				ht.insert(v.back());
			}
			cout << "insert over" << endl;
			for (const auto& e : v)
			{
				HashTable<int, int, KeyOfT<int>>::iterator it = ht.find(e);
				if (it != ht.end())
					ht.erase(e);
			}
			cout << "fine & erase over" << endl;

			cout << endl;
		}

		void Hash_Tree_Test()
		{
			cout << "Test" << endl;
			using namespace hash_bucket;
			srand((unsigned int)time(nullptr));
			vector<int> vp;
			for (int i = 0; i < 5000; ++i)
				vp.push_back(rand());

			set<int> mp;
			HashTable<int, int, KeyOfT<int>> hs;
			size_t begin = 0, end = 0;

			cout << "Tree" << endl;
			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				mp.insert(vp[i]);
			end = clock();
			cout << "size: " << mp.size() << endl;
			cout << "insert: " << end - begin << endl;
			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				mp.find(vp[i]);
			end = clock();
			cout << "find: " << end - begin << endl;
			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				mp.erase(vp[i]);
			end = clock();
			cout << "erase: " << end - begin << endl << endl;

			cout << "Hash" << endl;
			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				hs.insert(vp[i]);
			end = clock();

			cout << "size: " << hs.size() << endl;
			cout << "使用桶数量: " << hs.bucket_num() << endl;
			cout << "表长度: " << hs.table_size() << endl;
			size_t sum = 0;
			for (size_t i = 0; i <= hs.table_size(); ++i)
			{
				size_t tmp = hs.bucket_size(i);
				sum += tmp;
			}
			cout << "最大桶长度为: " << hs.max_bucket_count() << endl;
			printf("平均桶长度为: %.5f\n", (double)sum / hs.bucket_num());
			printf("荷载因子为: %.5f\n", (double)hs.size() / hs.table_size());
			cout << "insert: " << end - begin << endl;

			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				hs.find(vp[i]);
			end = clock();
			cout << "find: " << end - begin << endl;
			begin = clock();
			for (size_t i = 0; i < vp.size(); ++i)
				hs.erase(vp[i]);
			end = clock();
			cout << "erase: " << end - begin << endl;

			cout << endl;
		}

		void Unordered_Test()
		{
			cout << "Unordered_Test()" << endl;

			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			unordered_map<string, int> mp;
			for (const auto& e : vs)
				++mp[e];
			for (const auto& e : mp)
				cout << e.first << ":" << e.second << ' ';
			cout << endl;
			unordered_set<string> s(vs.begin(), vs.end());
			for (const auto& e : s)
				cout << e << ' ';

			cout << endl;
		}
		void UnorderedMulti_Test()
		{
			cout << "UnorderedMulti_Test()" << endl;

			vector<string> vs = { "苹果", "西瓜", "苹果", "西瓜", "香蕉", "梨子", "苹果", "西瓜", "西瓜", "香蕉" };
			unordered_multimap<string, int> mp;
			for (const auto& e : vs)
				mp.insert({ e, 1 });
			for (const auto& e : mp)
				cout << e.first << ":" << e.second << ' ';
			cout << endl;
			unordered_multiset<string> s(vs.begin(), vs.end());
			for (const auto& e : s)
				cout << e << ' ';

			cout << endl;
		}
	}
}
//int main() 
//{
//	try
//	{
//		//qyc::test::CloseHashTable_Test();
//		//qyc::test::HashBucket_Test();
//		qyc::test::Hash_Tree_Test();
//		qyc::test::Unordered_Test();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unkonw exception" << endl;
//	}
//	return 0;
//}

#include "bitset.h"
#include "BloomFilter.h"
//位图
//使用二进制比特位来存放某种状态,适用于海量数据,数据无重复的场景
//	通常是用来判断某个数据存不存在的
//位图的应用
//	快速查找某个数据是否在一个集合中
//	排序 + 去重
//	求两个集合的交集、并集等
//	操作系统中磁盘块标记
//缺点:	位图一般只能处理整形
//
//布隆过滤器
//由布隆(Burton Howard Bloom)在1970年提出的一种紧凑型的,比较巧妙的概率型数据结构
//	特点是高效地插入和查询,可以用来告诉用户 "某样东西一定不存在或者可能存在"
//使用多个哈希函数,将一个数据映射到位图结构中
//该方式可以提升查询效率,也可以节省大量的内存空间
//查找
//布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1
//所以可以按照以下方式进行查找:
//	分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中
//注意:
//	布隆过滤器若说某个元素不存在时,该元素一定不存在
//		若说某个元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判
//删除
//布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素
//一种支持删除的方法:
//	将布隆过滤器中的每个比特位扩展成一个小的计数器
//		插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一
//		删除元素时,给k个计数器减一
//	通过多占用几倍存储空间的代价来增加删除操作
//	缺陷:
//		无法确认元素是否真正在布隆过滤器中
//		存在计数回绕
//布隆过滤器优点
//	增加和查询元素的时间复杂度为:O(K),(K为哈希函数的个数,一般比较小),与数据量大小无关
//	哈希函数相互之间没有关系,方便硬件并行运算
//	布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
//	在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势
//	数据量很大时,布隆过滤器可以表示全集，其他数据结构不能
//	使用同一组散列函数的布隆过滤器可以进行交/并/差运算
//布隆过滤器缺陷
//	有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中
//		(补救方法:	再建立一个白名单,存储可能会误判的数据)
//	不能获取元素本身
//	一般情况下不能从布隆过滤器中删除元素
//	如果采用计数方式删除,可能会存在计数回绕问题
//测试实现bitset & BloomFilter
namespace qyc
{
	namespace test
	{
		void BitSet_Test()
		{
			cout << "BitSet_Test" << endl;
			bitset<20> bs;
			bs.set(10);
			bs.set(17);
			bs.set(6);

			cout << bs.test(10) << endl;
			cout << bs.test(6) << endl;
			bs.reset(6);
			cout << bs.test(6) << endl;

			bitset<UINT_MAX> bs1;
		}
		void BloomFilter_Test()
		{
			BloomFilter<string, 10> bf;
			vector<string> vs = { "苹果", "西瓜", "菠萝", "香蕉", "桃子", "梨子", "蓝莓", "香瓜", "樱桃", "椰子" };
			for (const auto& e : vs)
				bf.set(e);
			for (auto& str : vs)
				cout << bf.test(str) << ' ';
			cout << endl;
			vector<string> vs1 = { "苹果1", "西瓜", "菠萝1", "香蕉", "桃子1", "梨子", "蓝莓1", "香瓜", "樱桃1", "椰子" };
			for (const auto& e : vs1)
				cout << bf.test(e) << ' ';
			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		//qyc::test::BitSet_Test();
//		qyc::test::BloomFilter_Test();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unkonw exception" << endl;
//	}
//	return 0;
//}

//测试实现forward_list & deque
#include "forward_list.h"
#include "deque.h"
namespace qyc
{
	namespace test
	{
		void ForwardList_Test()
		{
			forward_list<vector<int>> x(5, vector<int>(1, 1));
			x.push_front(vector<int>(1, 2));
			x.insert_after(x.before_begin(), vector<int>(1, 0));
			for (auto& v : x)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			cout << endl;
			forward_list<vector<int>> x1(x);
			for (auto& v : x1)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			auto it = x.erase_after(x.begin());
			x.erase_after(it, x.end());
			cout << endl;
			x1 = x;
			for (auto& v : x)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			cout << endl;
			forward_list<pair<int, int>> fl;
			fl.emplace_front(1, 1);
		}
		void Deque_Deque()
		{
			deque<vector<int>> x(5, vector<int>(1, 1));
			x.push_front(vector<int>(1, 2));
			x.pop_front();
			x.insert(x.begin(), vector<int>(1, 0));
			for (auto& v : x)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			cout << endl;
			deque<vector<int>> x1(x);
			for (auto& v : x1)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			auto it = x.erase(x.begin());
			x.erase(it, x.end());
			cout << endl;
			x1 = x;
			for (auto& v : x)
			{
				for (auto& e : v)
					cout << e << ' ';
			}
			cout << endl;
		}
	}
}
//int main()
//{
//	try
//	{
//		qyc::test::Deque_Deque();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	catch (...)
//	{
//		cout << "unkonw exception" << endl;
//	}
//	return 0;
//}

//C++11
//2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称
//		不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准
//从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟
//相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正
//		这使得C++11更像是从C++98/03中孕育出的一种新语言
//相比较而言,C++11能更好地用于系统开发和库开发,语法更加泛华和简单化,更加稳定和安全
//	不仅功能更强大,而且能提升程序员的开发效率
//
//统一的列表初始化
//{ }初始化
//在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定
//C++11扩大了用大括号括起的列表(初始化列表)的使用范围
//	使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号 = ,也可不添加
//C++11中列表初始化也可以适用于new表达式中,创建对象时也可以使用列表初始化方式调用构造函数初始化
//
//std::initializer_list
//编译器看到{t1, t2, …, tn}便会做出一个initializer_list,它关联到一个array<T, n>
//调用构造函数的时候,{}内的元素会被编译器分解逐一传给函数
//但若函数的参数就是initializer_list,则是直接调用该参数的函数
//initializer_list内部并不负责保存初始化列表中的元素拷贝,仅仅是列表中元素的引用而己	通过过拷贝构造对象与原对象共享列表中的元素空间
//initializer_list与重载构造函数的关系:
//	当构造函数形参中不带initializer_list时,小括号和大括号的意义没有区别
//	如果构造函数中带有initializer_list形参,采用大括号初始化语法会强烈优先匹配带有initializer_list形参的重载版本,而其他更精确匹配的版本可能没有机会被匹配
//	空大括号构造一个对象时,表示没有参数(而不是空的initializer_list对象),会匹配默认的无参构造函数
//	拷贝构造函数和移动构造函数也可能被带有initializer_list形参的构造函数劫持
//使用场景:
//	std::initializer_list一般是作为构造函数的参数
//	C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了
//		也可以作为operator=的参数,这样就可以用大括号赋值
//
//auto
//在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型
//	但局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了
//C++11中废弃auto原来的用法,将其用于实现自动类型推断
//	要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型
//
//decltype
//关键字decltype将变量的类型声明为表达式指定的类型
//
//nullptr
//由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量
//所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针
//
//范围for循环
//其底层就是替换称为迭代器进行循环
//
//STL中变化
//	新容器:		array	forward_list	unordered_set	unordered_map
//	容器新方法:	cbegin	cend(返回const迭代器)等方法
//struct Point
//{
//	int _x;
//	int _y;
//};
//template<class T1, class T2>	// decltype的一些使用使用场景
//void F(T1 t1, T2 t2)
//{
//	decltype(t1 * t2) ret;
//	cout << typeid(ret).name() << endl;
//}
//int main()
//{
//	//列表初始化
//	int x1 = 1;
//	int x2{ 2 };
//	int array1[]{ 1, 2, 3, 4, 5 };
//	int array2[5]{ 0 };
//	Point p{ 1, 2 };
//	int* pa = new int[4] { 0 };
//
//	//initializer_list
//	auto t = { 1, 2, 3, 4 };
//	cout << typeid(t).name() << endl;
//
//	//decltype
//	const int x = 1;
//	double y = 2.2;
//	decltype(x * y) ret;	// ret的类型是double
//	decltype(&x) p1;			// p1的类型是int*
//	cout << typeid(ret).name() << endl;
//	cout << typeid(p1).name() << endl;
//	F(1, 'a');
//
//	return 0;
//}
//
//右值引用 & 移动语义
//左值引用和右值引用
//传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性
//		所以从现在开始我们之前学习的引用就叫做左值引用
//无论左值引用还是右值引用,都是给对象取别名
//
//左值 & 左值引用
//左值是一个表示数据的表达式(如变量名/解引用的指针)
//左值可以出现赋值符号的左边,右值不能出现在赋值符号左边,可以取地址,赋值
//定义时const修饰符后的左值,不能被赋值,但可以取地址
//左值引用就是给左值的引用,给左值取别名
//左值引用总结:
//	左值引用只能引用左值,不能引用右值
//	但是const左值引用既可引用左值,也可引用右值
//左值引用解决场景
//	做参数:		减少拷贝提高效率,做输出型参数
//	做返回值:	减少拷贝提高效率,引用返回可以修改返回对象(operator[])
//
//右值 & 是右值引用
//右值也是一个表示数据的表达式(如字面常量/表达式返回值/函数返回值(这个不能是左值引用返回)等等)
//右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,不能取地址
//右值引用就是对右值的引用,给右值取别名
//右值是不能取地址的,但给右值取别名后,右值会被存储到特定位置,且可以取地址,修改
//	若不想被修改,可以用 const T&& 去引用
//右值引用总结:
//	右值引用只能引用右值,不能引用左值
//	但是右值引用可以引用move以后的左值
//
//左值引用的短板:
//当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回
//右值引用和移动语义解决该问题:
//移动构造本质是将参数右值的资源窃取过来,占位已有
//就不用做深拷贝了,所以叫移动构造,就是窃取别人的资源来构造自己
//移动构造中没有新开空间,拷贝数据,效率提高
//右值引用引用左值
//按照语法,右值引用只能引用右值
//	有些场景下,可能需要用右值去引用左值实现移动语义
//当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值
//C++11中,std::move()
//它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义
//	注:	可能导致该左值的资源被释放
//template<class _Ty>
//inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
//{
//	// forward _Arg as movable
//	return ((typename remove_reference<_Ty>::type&&)_Arg);
//}
//
//新的类功能
//默认成员函数
//原来C++类中,有6个默认成员函数:
//		构造函数	析构函数	拷贝构造函数	拷贝赋值重载	取地址重载	const取地址重载(后两个用处不大)
//默认成员函数就是用户不写编译器会生成一个默认的
//C++11新增了两个:
//		移动构造函数	移动赋值运算符重载
//若用户没有自己实现移动构造函数/移动赋值运算符重载,且没有实现析构函数,拷贝构造,拷贝赋值重载中的任意一个
//	那么编译器会自动生成一个默认移动构造函数/移动赋值运算符重载
//默认生成的移动函数	对于内置类型成员会执行逐成员按字节拷贝
//						自定义类型成员,则要看该成员是否实现移动构造
//							若实现了,就调用移动构造,否则就调用拷贝构造
//若用户提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值
//class A
//{
//public:
//	A(const int a = 0)
//		: _a(a) {}
//	A(const A& aa) {
//		_a = aa._a;
//		cout << "构造函数深拷贝" << endl;
//	}
//	A(A&& aa) {
//		_a = aa._a;
//		cout << "构造函数资源转移" << endl;
//	}
//	A& operator=(const A& aa) {
//		_a = aa._a;
//		cout << "operator=深拷贝" << endl;
//		return *this;
//	}
//	A& operator=(A&& aa) {
//		_a = aa._a;
//		cout << "operator=资源转移" << endl;
//		return *this;
//	}
//private:
//	int _a;
//};
//int main()
//{	
//	//左值	可以出现在赋值操作符的左右两侧
//	int a = 10;
//	const int b = 20;
//	int* p = &a;
//	*p = 100;		//*p为左值
//	double x = 1.1, y = 2.2;
//	//左值引用
//	int& ra = a;
//	const int& cra = 10;	//const左值引用右值
//
//	//右值	不能出现在赋值操作符的左边
//	10;
//	x + y;
//	//右值引用
//	int&& rr1 = 10;
//	double&& rr2 = x + y;
//	int&& rra = std::move(a);		//右值引用move后的左值
//
//	qyc::string str("hello");
//	qyc::string _str(std::move(str));
//	cout << "str: " << str << endl;
//	cout << "_str: " << _str << endl;
//
//	list<A> va;
//	A aa = 1;
//	va.push_back(aa);		//构造函数深拷贝
//	cout << "------------------------" << endl;
//	va.push_back(1);		//构造函数资源转移
//	va.push_back(A(1));		//构造函数资源转移
//
//	return 0;
//}
//
//完美转发
//模板中的&& 万能引用
//模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值
//	万能引用只是提供了能够接收同时接收左值引用和右值引用的能力
//	但限制了接收的类型,后续使用中都退化成了左值
//若要能够在传递过程中保持它的左值或者右值的属性,就需要用到完美转发
//完美转发
//std::forward 完美转发在传参的过程中保留对象原生类型属性
//void Fun(int &x){ cout << "左值引用" << endl; }
//void Fun(const int& x) { cout << "const左值引用" << endl; }
//void Fun(int&& x) { cout << "右值引用" << endl; }
//void Fun(const int&& x) { cout << "const右值引用" << endl; }
//template<class T>
//void PerfectForward(T&& t)	//引用折叠/万能引用		t既能引用左值,也能引用右值
//{
//	Fun(t);
//}
//template<class T>
//void _PerfectForward(T&& t)
//{
//	Fun(qyc::forward<T>(t));
//}
//int main()
//{
//	//结果都为左值引用/const左值引用
//	int a = 0;
//	PerfectForward(a);				// 左值
//	PerfectForward(std::move(a));	// 右值
//	const int b = 8;
//	PerfectForward(b);				// const 左值
//	PerfectForward(std::move(b));	// const 右值
//	cout << "-----------------------" << endl;
//	_PerfectForward(a);
//	_PerfectForward(qyc::move(a));
//	_PerfectForward(b);
//	_PerfectForward(qyc::move(b));
//	return 0;
//}
//
//类成员变量初始化
//C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化
//
//继承和多态中的final & override
//	final:		修饰一个成员函数不能被重写,修饰一个类不能被继承
//	override:	检查派生类函数是否重写了基类的函数,未重写就编译报错
//
//强制生成默认函数的关键字default:
//C++11可以让用户更好的控制要使用的默认函数
//	假设要使用某个默认的函数,但由于一些原因该函数没有默认生成
//		那么可以使用default关键字显示指定生成
//	默认函数(参数) =default
//
//禁止生成默认函数的关键字delete:
//想要限制某些默认函数的生成
//	在C++98中,是该函数设置成private,并且只声明补丁已
//		这样只要其他人想要调用就会报错
//	在C++11中更简单,只需在该函数声明加上 =delete即可
//		该语法指示编译器不生成对应函数的默认版本,称 =delete修饰的函数为删除函数
//	默认函数(参数) =delete
//
//class AA
//{
//public:
//	AA()
//	{
//		cout << "AA" << endl;
//	}
//private:
//	int _aa;
//};
//class A : public AA
//{
//public:
//	A()
//		: _a(0)
//	{};
//	A(const int a = 0)
//		: _a(0)	//初始化参数列表有大坑
//	{
//		_a = a;
//	}
//	A(int* pa)
//	{
//		new(this)A(*pa);
//	}
//private:
//	int _a = 1;
//};
//int main()
//{
//	int c = 1;
//	int* pc = &c;
//	A a(pc);
//	qyc::hash_bucket::HashTable<int, int, qyc::test::KeyOfT<int>> ht = {1,2,3,4,4,5};
//	return 0;
//}
//
//可变参数模板
//C++98/03中类模板和函数模板中只能含固定数量的模板参数
//C++11的新特性可变参数模板能够创建可以接受可变参数的函数模板和类模板
// Args是一个模板参数包		args是一个函数形参参数包
// 声明一个参数包Args...args	这个参数包中可以包含0到任意个模板参数
//template <class ...Args>
//void ShowList(Args... args) {}
//参数args前面有省略号,所以它是一个可变模版参数
//	将带省略号的参数称为"参数包",它里面包含了0到N(N >= 0)个模版参数
//		sizeof...(args)		计算参数包中参数的个数
//我们无法直接获取参数包args中的每个参数,只能通过展开参数包的方式来获取参数包中的每个参数
//	但语法不支持使用args[i]这样方式获取可变参数
//展开可变模板参数包的方式
//		递归函数方式展开参数包
//		逗号表达式展开参数包
//			该展开参数包方式,无需通过递归终止函数,直接在expand函数体中展开
//			printarg不是递归终止函数,而是处理参数包中每一个参数的函数
//				这种就地展开参数包的方式实现的关键是逗号表达式
//			逗号表达式会按顺序从左向右执行,expand函数中的逗号表达式:	(printarg(args), 0)
//					先执行printarg(args),再得到逗号表达式的结果0
//					同时还用到了C++11的初始化列表,通过初始化列表来初始化一个变长数组
//			{ (printarg(args), 0)... }将会展开成((printarg(arg1), 0), (printarg(arg2), 0), (printarg(arg3), 0), etc...)
//				最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]
//			由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数
//				也就是在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包
//递归展开
//template <class T>
//void ShowList(const T& t)		// 递归终止函数
//{
//	cout << t << endl;
//}
//template <class T, class ...Args>
//void ShowList(T value, Args... args)		// 展开函数
//{
//	cout << value << " ";
//	ShowList(args...);
//}
////逗号表达式展开
//template<class T>
//int PrintArg(const T& t)
//{
//	cout << t << ' ';
//	return 0;
//}
//template<class... Args>
//void _ShowList(Args... args)
//{
//	int a[] = { PrintArg(args)... };
//	cout << endl;
//}
//int main()
//{
//	_ShowList(1);
//	_ShowList(1, 'A');
//	_ShowList(1, 'A', std::string("sort"), "hello", "world");
//	return 0;
//}
//
//lambda表达式
//在C++98中,如果想要对一个数据集合中的元素进行排序,可以使用std::sort方法
//如果待排序元素为自定义类型,需要用户定义排序时的比较规则
//随着C++语法的发展,上面的写法太复杂了,每次为了实现一个algorithm算法,都要重新去写一个类
//若每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便
//因此,在C++11语法中出现了Lambda表达式
//lambda表达式书写格式:	[capture-list] (parameters) mutable -> return-type { statement }
//	[capture-list]:	捕捉列表,该列表总是出现在lambda函数的开始位置
//						编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用
//	(parameters):	参数列表	与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
//	mutable:		默认情况下,lambda函数总是一个const函数(即捕捉到的变量不可被修改),mutable可以取消其常量性
//						使用该修饰符时,参数列表不可省略(即使参数为空)
//	-> returntype:	返回值类型	用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略
//						返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
//	{ statement }:	函数体	在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量
//
//捕获列表说明
//	捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用
//	[var]:	表示值传递方式捕捉变量var
//	[=]:	表示值传递方式捕获所有父作用域中的变量(包括this)
//	[&var]:	表示引用传递捕捉变量var
//	[&]:	表示引用传递捕捉所有父作用域中的变量(包括this)
//	[this]:	表示值传递方式捕捉当前的this指针
//注意:
//	父作用域指包含lambda函数的语句块
//	语法上捕捉列表可由多个捕捉项组成,并以逗号分割
//		例: [=, &a, &b]:	以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
//		例: [&，a, this]:	值传递方式捕捉变量a和this,引用方式捕捉其他变量
//	捕捉列表不允许变量重复传递,否则就会导致编译错误
//		例: [=, a]: =已经以值传递方式捕捉了所有变量,捕捉a重复
//	在块作用域以外的lambda函数捕捉列表必须为空
//	在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错
//	lambda表达式之间不能相互赋值,即使看起来类型相同
//		允许使用一个lambda表达式拷贝构造一个新的副本	auto copyname(destname);
//	可以将lambda表达式赋值给相同类型的函数指针
//注意:
//在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空
//因此C++11中最简单的lambda函数为:	[]{};	该lambda函数不能做任何事情
//lambda表达式实际上可以理解为匿名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量
//
//函数对象与lambda表达式
//函数对象,又称为仿函数,即可以像函数一样使用的对象,就是在类中重载了operator()运算符的类对象
//从使用方式上来看,函数对象与lambda表达式完全一样
//实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的
//	如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()
//auto fx = [] {};
//int main()
//{
//	fx();
//	auto f = [] {};
//	f();
//	auto e = [](int a, int b = 0) mutable -> int { return a + b; };
//	cout << e(1) << endl;
//	auto x = [] {};
//	x();
//	void (*pFunc)(int&, int&) = nullptr;
//	{
//		int a = 1, b = 2;
//		auto swp = [](int& left, int& right) mutable { int tmp = left; left = right; right = tmp; };
//		swp(a, b);
//		cout << a << ' ' << b << endl;
//		auto swp1 = swp;
//		auto swp2(swp);
//
//		{
//			auto cd = [=]() mutable { a++, b++; };
//			cd();
//		}
//
//		pFunc = swp;
//		pFunc(a, b);
//		cout << a << ' ' << b << endl;
//	}
//	int a = 1, b = 2;
//	pFunc(a, b);
//	cout << a << ' ' << b << endl;
//	return 0;
//}
//
//包装器
//function包装器	也称作适配器
//C++中的function本质是一个类模板,也是一个包装器
//std::function在头文件<functional>
//类模板原型如下
//template <class T> function; // undefined
//template <class Ret, class... Args>
//class function<Ret(Args...)>;
//模板参数说明:
//	Ret:		被调用函数的返回类型
//	Args...:	被调用函数的形参
//使用包装器可以解决模板实例化多份,效率底下的问题
//
//bind
//std::bind函数定义<functional>中
//是一个函数模板,像一个函数包装器(适配器)
//	接受一个可调用对象(callable object),生成一个新的可调用对象来适应原对象的参数列表
//一般而言,可以用它把一个原本接收N个参数的函数,通过绑定一些参数,返回一个接收M个(M可以大于N)参数的新函数
//	使用std::bind函数还可以实现参数顺序调整等操作
//原型如下:
//template <class Fn, class... Args>
//	/* unspecified */ bind(Fn&& fn, Args&&... args);
//	// with return type (2)
//template <class Ret, class Fn, class... Args>
//	/* unspecified */ bind(Fn&& fn, Args&&... args);
//可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表
//调用bind的一般形式:
//		auto newCallable = bind(callable, arg_list);
//newCallable:	本身是一个可调用对象
//arg_list:		是一个逗号分隔的参数列表,对应给定的callable的参数
//当调用newCallable时,newCallable会调用callable,,并传给它arg_list中的参数
//arg_list中参数
//	可能包含形如_n的名字,其中n是一个整数,这些参数是"占位符"
//		表示newCallable的参数,它们占据了传递给newCallable的参数的"位置"
//			数值n表示生成的可调用对象中参数的位置:	_1为newCallable的第一个参数,_2为第二个参数,以此类推
//template<class F, class T>
//T useF(F f, T x)
//{
//	static int count = 0;
//	cout << "count:" << ++count << endl;
//	cout << "count:" << &count << endl;
//	return f(x);
//}
//double f(double i)
//{
//	return i / 2;
//}
//struct Functor
//{
//	double operator()(double d)
//	{
//		return d / 3;
//	}
//};
//int add(int a, int b)
//{
//	return a + b;
//}
//class Sub
//{
//public:
//	int sub(int a, int b)
//	{
//		return a - b;
//	}
//};
//int main()
//{
//	//function使用
//	std::function<double(double)> func1 = f;		//包装函数
//	std::function<double(double)> func2 = Functor();	//包装仿函数
//	std::function<double(double)> func3 = [](double x) -> double { return x / 4; };		//包装lambda
//	cout << useF(func1, 2) << endl;
//	cout << useF(func2, 3) << endl;
//	cout << useF(func3, 4) << endl;
//	std::function<int(Sub, int, int)> func4 = &Sub::sub;	//包装成员函数
//	cout << func4(Sub(), 2, 1) << endl;
//	cout << endl;
//	
//	//bind使用
//	int a = 0, b = 0;
//	std::function<int(int, int)> _func1 = std::bind(add, placeholders::_1, placeholders::_2);
//	auto _func2 = std::bind(add, a, b);		//绑定固定值,常量/变量
//	Sub s = {};
//	std::function<int(int, int)> _func3 = std::bind(&Sub::sub, s, placeholders::_1, placeholders::_2);
//	std::function<int(int, int)> _func4 = std::bind(&Sub::sub, s, placeholders::_2, placeholders::_1);		//调换参数顺序
//	std::function<int(int, int)> _func5 = std::bind([](int a, int b)-> int { return a - b; }, 1, placeholders::_1);		//将1传给a,将_func5的参数1传给b
//	cout << _func1(2, 1) << endl;
//	cout << _func2(2, 2) << endl;
//	cout << _func3(2, 1) << endl;
//	cout << _func4(2, 1) << endl;
//	cout << _func5(3, 2) << endl;
//
//	return 0;
//}

//异常
//C语言传统的处理错误的方式
//传统的错误处理机制:
//	1. 终止程序			缺陷:用户难以接受如
//	2. 返回错误码		缺陷:需要程序员自己去查找对应的错误
//		如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误
//C语言基本使用返回错误码的方式处理错误,严重错误的情况下会使用终止程序处理
//
//C++异常	一种处理错误的方式
//当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误
//	throw: 当问题出现时,程序会抛出一个异常
//			通过使用throw关键字来完成
//	catch: 在要处理问题的地方,通过异常处理程序捕获异常
//			catch 关键字用于捕获异常,可以有多个catch进行捕获
//	try:   try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块
//如果有一个块抛出一个异常,捕获异常的方法会使用try和catch关键字
//	try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码
//
//异常的抛出和捕获
//异常的抛出和匹配原则
//1. 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码
//2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个
//3. 抛出异常对象后，会生成一个异常对象的拷贝(抛出的异常对象可能是一个临时对象)
//		这个拷贝的临时对象会在被catch以后销毁	(类似函数的传值返回)
//4. catch (...)可以捕获任意类型的异常,但不知道异常错误是什么
//5. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配
//		可以抛出的派生类对象,使用基类捕获
//
//在函数调用链中异常栈展开匹配原则
//1. 首先检查throw本身是否在try块内部,若是再查找匹配的catch语句
//		若有匹配的,则调到catch的地方进行处理
//2. 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
//3. 如果到达main函数的栈，依旧没有匹配的，则终止程序
//		栈展开: 沿着调用链查找匹配的catch子句的过程
//		实际中,最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止
//4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行
//
//异常的重新抛出
//有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后，希望再交给更外层的调用链函数来处理
//	catch则可以通过重新抛出将异常传递给更上层的函数进行处理
//
//异常安全
//构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常
//		否则可能导致对象不完整或没有完全初始化
//析构函数主要完成资源的清理,最好不要在析构函数内抛出异常
//		否则可能导致资源泄漏
//C++中异常经常会导致资源泄漏的问题		C++经常使用RAII来解决
//	在new和delete中抛出了异常,导致内存泄漏
//	在lock和unlock之间抛出了异常导致死锁
//
//异常规范
//异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。
//		可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型
//函数的后面接throw(),表示函数不抛异常
//C++11中新增的noexcept，表示不会抛异常
//若无异常接口声明,则此函数可以抛掷任何类型的异常
//
//自定义异常体系
//实际中,会自定义自己的异常体系进行规范的异常管理
//在一个项目中若随意抛异常,外层的调用者基本就没办法使用了
//所以会定义一套继承的规范体系,抛出的都是继承的派生类对象捕获一个基类即可
//
//C++标准库的异常体系
//C++提供了一系列标准的异常,定义在中,可以在程序中使用这些标准的异常,其以父子类层次结构组织起来的
//	std::exception {				是所有标准C++异常的父类
//		std::bad_alloc				可以通过new抛出
//		std::bad_cast				可以通过dynamic_cast抛出
//		std::bad_typeid				可以通过typeid抛出
//		std::bad_exception			在处理C++程序中无法预期的异常时非常有用
//		std::logic_error {			理论上可以通过读取代码来检测异常
//			std::domain_error		当使用了一个无效的数学域时抛出
//			std::invalid_argument	当使用了无效的参数时抛出
//			std::length_error		当创建了太长的std::string时抛出
//			std::out_of_range		可以通过方法抛出,如operator[]
//		}
//		std::runtime_error {		理论上不可以通过读取代码来检测到的异常
//			std::overflow_error		当发生数学上溢时抛出
//			std::range_error		当尝试存储超出范围的值时抛出
//			std::underflow_error	当发生数学下溢时抛出
//		}
//	}
//实际中可以去继承exception类实现自己的异常类
//
//异常的优点
//1. 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息
//		甚至可以包含堆栈调用的信息,可以更好的定位程序的bug
//2. 返回错误码的问题: 在函数调用链中,深层的函数返回了错误,得层层返回错误,最外层才能拿到错误
//3. 很多的第三方库都包含异常
//4. 部分函数使用异常更好处理
//异常的缺点
//1. 异常会导致程序的执行流乱跳,非常混乱,并且是运行时出错抛异常就会乱跳
//		会导致跟踪调试,分析程序时比较困难
//2. 异常会有一些性能的开销
//3. C++没有垃圾回收机制,资源需要自己管理
//		有了异常非常容易导致内存泄漏/死锁等异常安全问题,需要使用RAII来处理资源的管理问题
//4. C++标准库的异常体系定义得不好,导致各自定义各自的异常体系,非常的混乱
//5. 异常尽量规范使用,不能随意抛异常
//		异常规范有两点:		1. 抛出异常类型都继承自一个基类
//							2. 函数是否抛异常,抛什么异常,都使用 函数() throw(); 的方式规范化
//总结:
//异常总体而言,利大于弊,所以工程中鼓励使用异常
//int Division(int left, int right)
//{
//	if (right == 0)
//		throw "Division by zero condition";
//	else
//		return left / right;
//}
//void Func()
//{
//	int a = 0, b = 0;
//	cin >> a >> b;
//	cout << Division(a, b) << endl;
//}
//int main()
//{
//	try
//	{
//		Func();
//	}
//	catch (const char* errmsg)
//	{
//		cout << errmsg << endl;
//	}
//	catch (...)
//	{
//		cout << "unknow exception" << endl;
//	}
//	return 0;
//}

#include <memory>
//智能指针
//智能指针的使用及原理
//RAII	(Resource Acquisition Is Initialization)
//是一种利用对象生命周期来控制程序资源（内存,文件句柄,网络连接,互斥量等)的简单技术
//在对象构造时获取资源
//	控制对资源的访问使之在对象的生命周期内始终保持有效
//在对象析构的时候释放资源
//	实际上把管理一份资源的责任托管给了一个对象
//优点
//不需要显式地释放资源
//采用这种方式,对象所需的资源在其生命期内始终保持有效
//
//智能指针的原理
//	其本质就是一个类,模板类中还得需要重载 *, -> 运算符
//	1. RAII特性
//	2. 重载operator* 和opertaor->,具有像指针一样的行为
//
//std::auto_ptr
//C++98版本的库中就提供了auto_ptr的智能指针
//auto_ptr的实现原理:	管理权转移的思想
//	auto_ptr是一个失败的设计
//
//std::unique_ptr
//C++11中开始提供更靠谱的unique_ptr
//unique_ptr的实现原理:	简单粗暴的防拷贝
//C++11库才更新智能指针实现
//C++11出来之前,boost搞除了更好用的scoped_ptr	shared_ptr	weak_ptr
//C++11将boost库中智能指针精华部分吸收了过来
//	推出了unique_ptr	shared_ptr	weak_ptr
//
//std::shared_ptr
//C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
//shared_ptr的原理:		是通过引用计数的方式来实现多个shared_ptr对象之间共享资源
//	shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享
//	在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一
//	如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
//	如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了
//	引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源
//shared_ptr智能指针的线程安全
//	引用计数的加减是加锁保护的,是指向资源不是线程安全的
//	指向堆上资源的线程安全问题是访问的人处理的,智能指针不管
//	引用计数的线程安全问题,是智能指针要处理的
//shared_ptr的循环引用
//在循环引用的场景下,将节点内的指针改为weak_ptr
//weak_ptr不是常规智能指针,没有RAII,不支持直接管理资源
//	主要使用shared_ptr来构造,来解决shared_ptr的循环引用的问题
//shared_ptr和weak_ptr也可以定制删除器,已解决指针的各种场景的释放
//template<class T>
//class SmartPtr
//{
//public:
//	SmartPtr(T* ptr)
//		: _ptr(ptr)
//	{
//		++count;
//	}
//
//	T& operator*()
//	{
//		return *_ptr;
//	}
//	T* operator->()
//	{
//		return _ptr;
//	}
//
//	~SmartPtr()
//	{
//		cout << "delete" << endl;
//		if (_ptr)
//			delete _ptr;
//	}
//private:
//	T* _ptr = nullptr;
//};
//int div()
//{
//	int a = 0, b = 0;
//	cin >> a >> b;
//	if (b == 0)
//		throw invalid_argument("b == 0");
//	return a / b;
//}
//void Func()
//{
//	SmartPtr<int> sp1(new int);
//	SmartPtr<double> sp2(new double);
//	cout << div() << endl;
//}
//int main()
//{
//	//try
//	//{
//	//	Func();
//	//}
//	//catch (const exception& e)
//	//{
//	//	cout << e.what() << endl;
//	//}
//	//catch (...)
//	//{
//	//	cout << "unkonw exception" << endl;
//	//}
//	
//	auto_ptr<int> pi1(new int);
//	auto_ptr<int> pi2(pi1);		//管理权转移,导致被拷贝对象悬空	pi1变为nullptr
//
//	return 0;
//}
//
#include "memory.h"
//测试实现智能指针
namespace qyc
{
	namespace test
	{
		void AutoPtr_Test()
		{
			cout << "AutoPtr_Test" << endl;

			auto_ptr<pair<int, int>> ap1(new pair<int, int>(0, 0));
			auto_ptr<pair<int, int>> ap2;
			++ap1->first;
			++ap1->second;
			ap2 = ap1;
			cout << ap2->first << ' ' << ap2->second << endl;
			ap2 = ap2;
			auto_ptr<pair<int, int>> ap3 = ap2;
			++ap3->first;
			++ap3->second;
			cout << ap3->first << ' ' << ap3->second << endl;
			
			cout << endl;
		}
		void UniquePtr_Test()
		{
			cout << "UniquePtr_Test" << endl;
			
			unique_ptr<pair<int, int>> ap1(new pair<int, int>(0, 0));
			unique_ptr<pair<int, int>> ap2;
			++ap1->first;
			++ap1->second;
			ap2.reset(ap1.release());
			cout << ap2->first << ' ' << ap2->second << endl;
			unique_ptr<pair<int, int>> ap3(ap2.release());
			++ap3->first;
			++ap3->second;
			cout << ap3->first << ' ' << ap3->second << endl;
			
			cout << endl;
		}
		void SharedPtr_Test()
		{
			cout << "SharedPtr_Test" << endl;
			shared_ptr<pair<int, int>> ap1(new pair<int, int>(0, 0));
			shared_ptr<pair<int, int>> ap2;
			++ap1->first;
			++ap1->second;
			ap2 = ap1;
			cout << ap2->first << ' ' << ap2->second << endl;
			ap2 = ap2;
			ap1 = ap2;
			shared_ptr<pair<int, int>> ap3(ap2);
			++ap3->first;
			++ap3->second;
			cout << ap3->first << ' ' << ap3->second << endl;
			shared_ptr<pair<int, int>> ap4(new pair<int, int>(0, 0));

			cout << endl;
		}
		struct Node
		{
			Node()
			{}
			~Node() {
				cout << "~Node" << endl;
			}
			weak_ptr<Node> _next;
			weak_ptr<Node> _prev;
		};
		void SharedPtr_RecycleTest()
		{
			shared_ptr<Node> n1(new Node);
			shared_ptr<Node> n2(new Node);
			
			n1->_next = n2;
			n2->_prev = n1;
		}
	}
}
//int main()
//{
//	qyc::test::AutoPtr_Test();
//	qyc::test::UniquePtr_Test();
//	qyc::test::SharedPtr_Test();
//	qyc::test::SharedPtr_RecycleTest();
//	return 0;
//}

//特殊类设计
//设计模式	(Design Pattern)
//	是一套被反复使用,多数人知晓的,经过分类的,代码设计经验的总结
//使用设计模式的目的:
//	为了代码可重用性,让代码更容易被他人理解,保证代码可靠性
//	设计模式使代码编写真正工程化
//
//设计一个不能被拷贝的类
//拷贝只会发生在两个场景中:		拷贝构造函数 赋值运算符重载
//	要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可
//C++98
//	将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有
//	原因:
//		设置成私有:		只声明没有设置成private,用户可以手动在类外定义
//		只声明不定义:	不定义是因为该函数根本不会调用,若定义了就不能防止成员函数内部拷贝
//C++11
//	C++11扩展delete的用法,在默认成员函数后跟上= delete,表示让编译器删除掉该默认成员函数
//
//设计一个只能在堆上创建对象的类
//实现方式:
//	将类的构造函数私有,拷贝构造声明成私有/=delete,防止别人调用拷贝在栈上生成对象
//	提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
//
//设计一个只能在栈上创建对象的类
//	同上将构造函数私有化,设计静态方法创建对象返回
//	禁掉operator new可以把下面用new调用拷贝构造申请对象给禁掉
//	void* operator new(size_t size) = delete;
//	void operator delete(void* p) = delete;
//
//请设计一个类，不能被继承
//C++98
//	构造函数私有化,派生类中调不到基类的构造函数,则无法继承
//C++11方法
//	使用final修饰类,表示该类不能被继承
//
//请设计一个类，只能创建一个对象(单例模式)
//单例模式:
//一个类只能创建一个对象,即单例模式
//该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享
//单例模式有两种实现模式
//饿汉模式
//	不论是否会用到,程序启动时就创建一个唯一的实例对象
//优点:	简单
//缺点:	可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定
//如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高
//	那么使用饿汉模式来避免资源竞争,提高响应速度更好
//
//懒汉模式
//	若单例对象构造十分耗时或者占用很多资源,而有可能该对象程序运行时不会用到
//		若也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢
// 所以这种情况使用懒汉模式(延迟加载)更好
//优点:	第一次使用实例对象时,创建对象,进程启动无负载,多个单例实例启动顺序自由控制
//缺点:	复杂
//class Singleton		//饿汉模式
//{
//public:
//	static Singleton* GetInstance()
//	{
//		//双检查加锁
//		if (pInstance == nullptr)
//		{
//			lock_guard<mutex> lg(_mtx);
//			if (pInstance == nullptr)
//				pInstance = new Singleton;
//		}
//		return pInstance;
//	}
//private:
//	Singleton() {};
//	Singleton(const Singleton&) = delete;
//	Singleton& operator=(const Singleton&) = delete;
//
//	static Singleton* pInstance;
//	static mutex _mtx;
//};
//Singleton* Singleton::pInstance = nullptr;
//mutex Singleton::_mtx;
//
//class _Singleton		//懒汉模式
//{
//public:
//	atomic<_Singleton>* GetInstance()
//	{
//		//双检查加锁
//		static atomic<_Singleton> ret;
//		return &ret;
//	}
//private:
//	_Singleton() {};
//	_Singleton(const _Singleton&) = delete;
//	_Singleton& operator=(const _Singleton&) = delete;
//};

//类型转换
//C语言中的类型转换
//在C语言中	若赋值运算符左右两侧类型不同
//			形参与实参类型不匹配
//			返回值类型与接收返回值类型不一致时		就需要发生类型转化
//C语言中总共有两种形式的类型转换:	隐式类型转换	显式类型转换
//	隐式类型转化:	编译器在编译阶段自动进行,能转就转,不能转就编译失败
//	显式类型转化:	需要用户手动处理(强制类型转换)
//缺陷:
//转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
//	隐式类型转化有些情况下可能会出问题
//	显式类型转换将所有情况混合在一起,代码不够清晰
//
//C++强制类型转换
//C++要兼容C语言,所以C++中还可以使用C语言的转化风格
//标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
//		static_cast		reinterpret_cast		const_cast		dynamic_cast
//static_cast
//static_cast用于非多态类型的转换(静态转换)
//编译器隐式执行的任何类型转换都可用static_cast
//	但不能用于两个不相关的类型进行转换
//reinterpret_cast
//reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释
//	用于将一种类型转换为另一种不同的类型
//const_cast
//const_cast最常用的用途就是删除变量的const属性,方便赋值
//const_cast中类型必须是指针引用或指向对象类型成员的指针
//int main()
//{
//	volatile const int a = 2;		//volatile修饰const类型,让编译器不对其优化
//	int* p = const_cast<int*>(&a);
//	*p = 3;
//	cout << a << endl;		//每次使用都要去内存在拿取
//	cout << *p << endl;
//
//	const int b = 2;		//const类型编译器会直接将其优化为常量
//	p = const_cast<int*>(&b);
//	*p = 3;
//	cout << b << endl;		//在使用的地方直接转为常量值
//	cout << *p << endl;
//	return 0;
//}
//dynamic_cast
//dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
//向上转型:	子类对象指针/引用 -> 父类指针/引用(不需要转换，赋值兼容规则)
//向下转型:	父类对象指针/引用 -> 子类指针/引用(用dynamic_cast转型是安全的)
//注意:
//	dynamic_cast只能用于父类含有虚函数的类
//	dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
//强制类型转换会关闭或挂起了正常的类型检查
//	每次使用强制类型转换前,应该仔细考虑是否还有其他不同的方法达到同一目的
//							若非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会
//RTTI	(Run-time Type identification)
//运行时类型识别
//C++通过以下方式来支持RTTI:
//	typeid运算符
//	dynamic_cast运算符
//	decltype

#include <fstream>
#include <sstream>
//C++IO流
//C语言的输入与输出
//C语言中的输入输出方式就是scanf()与printf()
//	scanf():	从标准输入设备(键盘)读取数据,并将值存放在变量中
//	printf():	将指定的文字/字符串输出到标准输出设备(屏幕)	注意宽度输出和精度输出控制
//C语言借助了相应的缓冲区来进行输入与输出
//	设	->	输入缓冲区	->	代码
//	备	->	输出缓冲区	->	内存
//对输入输出缓冲区的理解：
//	可以屏蔽掉低级I/O的实现,低级I/O的实现依赖操作系统本身内核的实现
//		所以屏蔽这部分的差异,就可以很容易写出可移植的程序
//	可以使用这部分的内容实现"行"读取的行为
//		对于计算机而言是没有"行"这个概念，有了这部分,去解析缓冲区的内容,就可以返回一个"行
//
//流	即是物质从一处向另一处流动的过程
//是对一种有序连续且具有方向性的数据(其单位可以是bit/byte/packet)的抽象描述
//C++流是指信息	从外部输入设备(如键盘)向计算机内部(如内存)输入
//			  和从内存向外部输出设备(显示器)输出的过程
//特性:	有序连续,具有方向性
//C++定义了I/O标准类库,这些每个类都称为流/流类,用以完成某方面的功能
//
//C++IO流
//C++系统实现了一个庞大的类库,其中ios为基类,其他类都是直接或间接派生自ios类
//C++标准IO流
//C++标准库提供了4个全局流对象cin/cout/cerr/clog
//	cout:	标准输出,即数据从内存流向控制台(显示器)
//	cin:	标准输入即数据通过键盘输入到程序中
//	cerr:	用来进行标准错误的输出
//	clog:	进行日志的输出
//cout,cerr,clog是ostream类的三个不同的对象
//	这三个对象现在基本没有区别,只是应用场景不同
//在使用时候必须要包含文件并引入std标准命名空间
//注意:
//	cin为缓冲流,键盘输入的数据保存在缓冲区中,当要提取时,从缓冲区中拿
//		如果一次输入过多,会暂留在缓冲区,只有把输入缓冲区中的数据取完后,才要求输入新的数据
//	输入的数据类型必须与要提取的数据类型一致,否则出错
//		出错只是在流的状态字state中对应位置置1,程序继续
//	空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入
//		但如果是字符型和字符串,则空格/回车无法用cin输入
//	cin和cout可以直接输入和输出内置类型数据
//		标准库已经将所有内置类型的输入和输出全部重载了
//对于自定义类型,若要支持cin和cout的标准输入输出,需要对<<和>>进行重载
//在线OJ中的输入和输出:
//	对于IO类型的算法,一般都需要循环输入
//	严格按照题目的要求进行输出,多一个少一个空格都不行
//	连续输入时,vs系列编译器下在输入ctrl + Z时结束
//istream类型对象转换为逻辑条件判断值
//	istream& operator>>(int& val);
//	explicit operator bool() const;
//istream类型的对象,可以做逻辑条件值,源自于istream的对象又调用了operator bool
//	operator bool调用时如果接收流失败,或者有结束标志就返回false
//C++ IO流,使用面向对象+运算符重载的方式更好的兼容自定义类型的流插入和流提取
//
//C++文件IO流
//C++根据文件内容的数据格式分为二进制文件和文本文件
//采用文件流对象操作文件的一般步骤：
//	定义一个文件流对象
//		ifstream ifile(只输入用)	ofstream ofile(只输出用)	fstream iofile(既输入又输出用)
//	使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
//	使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
//	关闭文件
//
//stringstream
//在C语言中,将一个整形变量的数据转化为字符串格式
//	使用itoa()函数	使用sprintf()函数
//但是两个函数在转化时,都得需要先给出保存结果的空间
//	而且转化格式不匹配时,可能还会得到错误的结果甚至程序崩溃
//在C++中,可以使用stringstream类对象来避开此问题
//使用stringstream必须要包含头文件
//	在该头文件下,标准库三个类:
//		istringstream/ostringstream/stringstream,分别用来进行流的输入,输出和输入输出操作
//stringstream主要可以用来:
//	将数值类型数据格式化为字符串
//	字符串拼接
//	序列化和反序列化结构数据
//stringstream实际是在其底层维护了一个string类型的对象用来保存结果
//多次数据类型转化时,一定要用clear()来清空,才能正确转化
//		但clear()不会将stringstream底层的string对象清空
//可以使用s.str("")方法将底层string对象设置为""空字符串
//可以使用s.str()将让stringstream返回其底层的string对象
//stringstream使用string类对象代替字符数组,可以避免缓冲区溢出的危险,而且其会对参数类型进行推演
//	不需要格式化控制,也不会出现格式化失败的风险,因此使用更方便,更安全
//struct Date
//{
//	int _year = 1;
//	int _month = 1;
//	int _day = 1;
//};
//istream& operator>>(istream& in, Date& d)
//{
//	return in >> d._year >> d._month >> d._day;
//}
//ostream& operator<<(ostream& out, const Date& d)
//{
//	return out << d._year << ' ' << d._month << ' ' << d._day;
//}
//struct ServerInfo
//{
//	string _address;
//	int _prot = 1;
//	Date _date;
//};
//struct ConfigManager
//{
//	ConfigManager(const string& filename = ".\\ServerInfo.txt")
//		: _filename(filename)
//	{}
//
//	void write_text(const ServerInfo& info)
//	{
//		ofstream ofs(_filename, ios_base::out);
//		ofs << info._address << ' ' << info._prot << ' ' << info._date << endl;
//	}
//	void read_test(ServerInfo& info)
//	{
//		ifstream ifs(_filename, ios_base::in | ios_base::binary);
//		ifs >> info._address >> info._prot >> info._date;
//	}
//private:
//	string _filename;
//};
//void FileIO()
//{
//	cout << "FileIO" << endl;
//	ServerInfo winfo = { "127.0.0.1", 111, { 23, 1, 1 } };
//	ConfigManager cm;
//	cm.write_text(winfo);
//
//	ServerInfo rinfo;
//	cm.read_test(rinfo);
//	cout << rinfo._address << ' ' << rinfo._prot << ' ' << rinfo._date << endl;
//	
//	//ifstream ifs("utility.h");
//	//for (char ch = ifs.get(); ifs; ch = ifs.get())
//	//{
//	//	cout << ch;
//	//}
//	//ifs.close();
//	//cout << endl;
//}
//void StringStream()
//{
//	cout << "StringStream" << endl;
//	ServerInfo winfo = { "127.0.0.1", 111, { 23, 1, 1 } };
//	ostringstream oss;
//	//序列化
//	oss << winfo._address << ' ' << winfo._prot << ' ' << winfo._date;
//
//	string str = oss.str();
//	cout << str << endl;
//
//	ServerInfo rinfo;
//	istringstream iss(str);
//	//反序列化
//	iss >> rinfo._address >> rinfo._prot >> rinfo._date;
//	cout << rinfo._address << ' ' << rinfo._prot << ' ' << rinfo._date << endl;
//	/*
//	int a = 12345678;
//	int b = 0;
//	string s1;
//	string s2;
//	stringstream ss;
//	ss << a;
//	ss >> b;
//	ss.clear();	// 注意多次转换时,必须使用clear将上次转换状态清空掉
//				// stringstreams在转换结尾时(即最后一个转换后),会将其内部状态设置为badbit
//				// 因此下一次转换是必须调用clear()将状态重置为goodbit才可以转换,不清空会转化失败
//				// 但clear()不会将stringstreams底层字符串清空掉
//	ss.str("");	//将stringstream底层管理string对象设置成"",否则多次转换时,会将结果全部累积在底层string对象中
//	ss << a;
//	ss >> s1;
//	s2 = ss.str();	//str()方法:	返回stringsteam中管理的string类型
//	cout << s1 << ' ' << s2 << endl;
//	cout << b << endl;
//	*/
//}
//int main()
//{
//	FileIO();
//	StringStream();
//	return 0;
//}

//空间配置器
//为各个容器高效的管理空间的(空间的申请与回收)
//
//通过new申请有以下不足:
//	空间申请与释放需要用户自己管理,容易造成内存泄漏
//	频繁向系统申请小块内存块,容易造成内存碎片,影响程序运行效率
//	直接使用malloc与new进行申请,每块空间前有额外空间浪费
//	无申请空间失败的应对操作
//	代码结构比较混乱,代码复用率不高,且未考虑线程安全问题
//因此需要设计一块高效的内存管理机制
//
//SGI-STL空间配置器实现原理
//new的不足之处,主要是,频繁向系统申请小块内存造成的
//SGI-STL以128作为小块内存与大块内存的分界线,将空间配置器其分为两级结构
//	一级空间配置器处理大块内存
//	二级空间配置器处理小块内存
//一级空间配置器
//	直接对malloc与free进行了封装，并增加了C++中set_new_handle思想
//二级空间配置器
//	负责处理小于128字节的小块内存
//	SGI-STL采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费
//		采用哈希桶的方式来提高用户获取空间的速度与高效管理
//内存池
//先申请一块比较大的内存块已做备用
//	当需要内存时,直接到内存池中去去,当池中空间不够时,再向内存中去取
//	当用户不用时,直接还回内存池即可
//避免了频繁向系统申请小块内存所造成的效率低,内存碎片以及额外浪费的问题
//SGI-STL中二级空间配置器设计
//二级空间配置器使用了内存池技术
//	但没有采用链表的方式对用户已经归还的空间进行管理(因为用户申请空间时在查找合适的小块内存时效率比较低)
//		而是采用了哈希桶的方式进行管理
//不需要128桶个空间来管理用户已经归还的内存块
//	因为用户申请的空间基本都是4的整数倍,其他大小的空间几乎很少用到
//因此:	SGI-STL将用户申请的内存块向上对齐到了8的整数倍(为了满足内存对齐)

#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>
//多线程
//thread类的简单介绍
//在C++11之前,涉及到多线程问题,都是和平台相关的,使得代码的可移植性比较差
//C++11是对线程进行了支持,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念
//要使用标准库中的线程,必须包含<thread>头文件
//thread()							构造一个线程对象,没有关联任何线程函数,即没有启动任何线程
//thread(fn, args1, args2, ...)		构造一个线程对象,并关联线程函数fn, args1,args2,...为线程函数的参数
//get_id()							获取线程id
//jionable()						线程是否还在执行,joinable代表的是一个正在执行中的线程
//jion()							该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行
//detach()							在创建线程对象后马上调用,用于把被创建线程与线程对象分离开
//										分离的线程变为后台线程,创建的线程的"死活"就与主线程无关
//注意:
//	线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态
//	当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程
//get_id()的返回值类型为id类型
//	id类型实际为std::thread命名空间下封装的一个类,该类中包含了一个结构体
//typedef struct		//vs下查看
//{ /* thread identifier for Win32 */
//	void* _Hnd; /* Win32 HANDLE */
//	unsigned int _Id;
//} _Thrd_imp_t;
//
//当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行
//线程函数一般情况下可按照以下三种方式提供:
//		函数指针	lambda表达式	函数对象
//
//thread类是防拷贝的,不允许拷贝构造以及赋值,但可以移动构造和移动赋值
//	即:将一个线程对象关联线程的状态转移给其他线程对象,转移期间不影响线程的执行
//可以通过jionable()函数判断线程是否是有效的
//如果是以下任意情况,则线程无效
//		采用无参构造函数构造的线程对象
//		线程对象的状态已经转移给其他线程对象
//		线程已经调用jion或者detach结束
//
//线程函数参数
//线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的
//	因此:	即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参
//若想要通过形参改变外部实参时,须借助std::ref()函数,或使用地址传参
//注意:	如果是类成员函数作为线程参数时,必须将this作为线程函数参数
//
//原子性操作库(atomic)
//多线程最主要的问题是共享数据带来的问题(即线程安全)
//如果共享数据都是只读的,就没问题,只读操作不会影响到数据,不会涉及对数据的修改,所以所有线程都会获得同样的数据
//但当一个或多个线程要修改共享数据时,就会产生很多潜在的麻烦
//C++98中传统的解决方式:
//	可以对共享修改的数据可以加锁保护
//		#include <mutex>
//		std::mutex m;
//		m.lock();
//		//线程数据操作
//		m.unlock();
//虽然加锁可以解决,但是加锁有一个缺陷就是:
//	只要一个线程在对数据操作时,其他线程就会被阻塞,会影响程序运行的效率,而且锁如果控制不好,还容易造成死锁
//因此C++11中引入了原子操作
//所谓原子操作:	即不可被中断的一个或一系列操作,C++11引入的原子操作类型,使得线程间数据的同步变得非常高效
//注意需要使用原子操作变量时,必须添加头文件
//	原子类型名称		对应的内置类型名称
//	atomic_bool			bool
//	atomic_char			char
//	atomic_schar		signed char
//	atomic_uchar		unsigned char
//	atomic_char16_t		char16_t
//	atomic_char32_t		char32_t
//	atomic_wchar_t		wchar_t
//	atomic_int			int
//	atomic_uint			unsigned int
//	atomic_short		short
//	atomic_ushort		unsigned short
//	atomic_long			long
//	atomic_ulong		unsigned long
//	atomic_llong		long long
//	atomic_ullong		unsigned long long
//在C++11中,不需要对原子类型变量进行加锁解锁操作,线程能够对原子类型变量互斥的访问
//更为普遍的,可以使用atomic类模板,定义出需要的任意原子类型
//atmoic<T> t;		// 声明一个类型为T的原子类型变量t
//注意:
//原子类型通常属于"资源型"数据,多个线程只能访问单个原子类型的拷贝
//因此在C++11中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造,移动构造以及operator=等
//为了防止意外,标准库已经将atmoic模板类中的拷贝构造,移动构造,赋值运算符重载默认删除掉了
//
//lock_guard与unique_lock
//在多线程环境下,若想保证某个变量的安全性,只要将其设置成对应的原子类型即可
//即高效又不容易出现死锁问题
//但是有些情况下,可能需要保证一段代码的安全性,那么只能通过锁的方式来进行控制
//缺陷:
//	锁控制不好时,可能会造成死锁,最常见的比如在锁中间代码返回,或者在锁的范围内抛异常
//因此:	C++11采用RAII的方式对锁进行了封装,即lock_guard和unique_lock
//
//mutex的种类
//在C++11中,mutex总共包含四个互斥量的种类:
//
//std::mutex
//C++11提供的最基本的互斥量,该类的对象之间不能拷贝,也不能进行移动
//mutex最常用的三个函数
//	lock()		上锁:	锁住互斥量
//	unlock()	解锁:	释放对互斥量的所有权
//	try_lock()	尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞
//线程函数调用lock()时,可能会发生以下三种情况:
//	该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁
//	当前互斥量被其他线程锁住,则当前的调用线程被阻塞住
//	当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)
//线程函数调用try_lock()时,可能会发生以下三种情况:
//	当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用unlock释放互斥量
//	当前互斥量被其他线程锁住,则当前调用线程返回false,而并不会被阻塞掉
//	当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)
//
//std::recursive_mutex
//其允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权
//释放互斥量时需要调用与该锁层次深度相同次数的unlock()
//除此之外,std::recursive_mutex的特性和std::mutex大致相同
//
//std::timed_mutex
//比std::mutex多了两个成员函数	try_lock_for()	try_lock_until()
//try_lock_for()
//接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住
//	如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁
//	如果超时(即在指定时间内还是没有获得锁),则返回false
//		与std::mutex的try_lock()不同,try_lock如果被调用时没有获得锁则直接返回false
//try_lock_until()
//接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住
//	如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁
//	如果超时(即在指定时间内还是没有获得锁),则返回false
//
//std::recursive_timed_mutex
//std::recursive_mutex与std::std::timed_mutex的结合
//
//lock_guard
//std::lock_gurad是C++11中定义的模板类
//lock_guard类模板主要是通过RAII的方式,对其管理的互斥量进行了封装
//	在需要加锁的地方,只需要用任意互斥体实例化一个lock_guard,调用构造函数成功上锁
//	出作用域前,lock_guard对象要被销毁,调用析构函数自动解锁,可以有效避免死锁问题
//lock_guard的缺陷:
//	太单一,用户没有办法对该锁进行控制
//
//unique_lock
//与lock_gard类似,unique_lock类模板也是采用RAII的方式对锁进行了封装
//	并且也是以独占所有权的方式管理mutex对象的上锁和解锁操作
//		即其对象之间不能发生拷贝
//在构造(或移动(move)赋值)时,unique_lock对象需要传递一个Mutex对象作为它的参数
//	新创建的unique_lock对象负责传入的Mutex对象的上锁和解锁操作
//使用以上类型互斥量实例化unique_lock的对象时,自动调用构造函数上锁
//	unique_lock对象销毁时自动调用析构函数解锁,可以很方便的防止死锁问题
//与lock_guard不同的是,unique_lock更加的灵活,提供了更多的成员函数:
//上锁/解锁操作:	lock	try_lock	try_lock_for	try_lock_until	unlock
//修改操作:			移动赋值	交换(swap:与另一个unique_lock对象互换所管理的互斥量所有权)	释放(release:返回它所管理的互斥量对象的指针,并释放所有权)
//获取属性:			owns_lock(返回当前对象是否上了锁)	operator bool()(与owns_lock()的功能相同)	mutex(返回当前unique_lock所管理的互斥量的指针)
//
//void Mutex_Test()
//{
//	cout << "Mutex_Test" << endl;
//	int a = 10;
//	int count = 0;
//	mutex mtx;
//	std::function<void()> f1 = std::bind([&]() -> void
//		{
//			for (int i = 0; i < a; ++i)
//			{
//				mtx.lock();
//				cout << this_thread::get_id() << ':' << i << endl;
//				std::this_thread::sleep_for(std::chrono::milliseconds(100));
//				++count;
//				mtx.unlock();
//			}
//		});
//	int n = 0;
//	cin >> n;
//	vector<thread> v(n);
//	for (auto& e : v)
//		e = thread(f1);
//	for (auto& e : v)
//		e.join();
//	cout << count << endl;
//
//	cout << endl;
//}
//void Atomic_Test()
//{		
//	cout << "Atomic_Test" << endl;
//	int a = 10000;
//	atomic<int> count = 0;
//	std::function<void()> f = std::bind([&]() -> void
//		{
//			for (int i = 0; i < a; ++i)
//				++count;
//		});
//
//	int n = 0;
//	cin >> n;
//	vector<thread> v(n);
//	for (auto& e : v)
//		e = thread(f);
//	for (auto& e : v)
//		e.join();
//	cout << count << endl;
//	cout << endl;
//}
//void LockGuard()
//{
//	cout << "LockGuard" << endl;
//	mutex mtx;
//	function<void()> f = bind([&]() -> void
//		{
//			lock_guard<mutex> x(mtx);
//		});
//
//	int n = 100;
//	vector<thread> v(n);
//	for (auto& e : v)
//		e = thread(f);
//	for (auto& e : v)
//		e.join();
//
//	cout << endl;
//}
//void ThreadPrint()
//{
//	cout << "ThreadPrint" << endl;
//	int i = 0;
//	int n = 100;
//	thread t1([&]() -> void
//		{
//			while (i < n)
//			{
//				while (i % 2 != 0)
//					this_thread::yield();
//				cout << this_thread::get_id() << ":" << i << endl;;
//				++i;
//			}
//		});
//	thread t2([&]() -> void
//		{
//			while (i < n)
//			{
//				while (i % 2 == 0)
//					this_thread::yield();
//				cout << this_thread::get_id() << ":" << i << endl;;
//				++i;
//			}
//		});
//	t1.join();
//	t2.join();
//
//	cout << endl;
//}
//void ConditionVariable()
//{
//	cout << "ConditionVariable" << endl;
//	
//	volatile int i = 0;
//	const int n = 100;
//	condition_variable cv;
//	mutex mtx;
//	volatile bool ready = true;
//	
//	thread t1([&]() -> void
//		{
//			while (i < n)
//			{
//				unique_lock<mutex> ulock(mtx);
//				//wait会先解锁,lambda返回true线程就运行,返回false线程就wait
//				cv.wait(ulock, [&ready, &i]() -> bool { return i % 2 == 1;/*return !ready;*/ });
//				cout << this_thread::get_id() << ':' << i << endl;
//				++i;
//				ready = true;
//				cv.notify_one();		//notify_one唤醒下一个线程,若无线程时,该函数则什么都不做
//			}
//		});
//	thread t2([&]() -> void
//		{
//			while (i < n)
//			{
//				unique_lock<mutex> ulock(mtx);
//				cv.wait(ulock, [&ready, &i]() -> bool { return i % 2 == 0;/*return ready;*/ });
//				cout << this_thread::get_id() << ':' << i << endl;
//				++i;
//				ready = false;
//				cv.notify_one();
//			}
//		});
//	
//	this_thread::sleep_for(chrono::milliseconds(1000));
//	cout << "t1: " << t1.get_id() << endl;
//	cout << "t2: " << t2.get_id() << endl;
//
//	t1.join();
//	t2.join();
//
//	cout << endl;
//}
//void SharePtr_Thread()
//{
//	qyc::shared_ptr<double> sp1(new double(1.11));
//	qyc::shared_ptr<double> sp2(sp1);
//
//	vector<thread> v(2);
//	int n = 10000;
//	for (auto& e : v)
//	{
//		e = thread([&]() -> void
//			{
//				for (int i = 0; i < n; ++i)
//					qyc::shared_ptr<double> sp(sp1);
//			});
//	}
//	for (auto& e : v)
//		e.join();
//	cout << sp1.use_count() << endl;
//	cout << endl;
//}
//int main()
//{
//	//Mutex_Test();
//	//Atomic_Test();
//	//LockGuard();
//	//ThreadPrint();
//	//ConditionVariable();
//	//SharePtr_Thread();
//	return 0;
//}

//派生类中using引入被隐藏的基类成员
//class base
//{
//public:
//	void fun()
//	{
//		cout << "base" << endl;
//	}
//};
//class drive : public base
//{
//public:
//	using base::fun;
//	void fun(int i)
//	{
//		cout << "drive" << i << endl;
//	}
//};
//alignas & alignof
//alignas	设置内存中对齐方式,最小为8字节
//struct alignas(1) test1
//{
//	char a;
//};
//struct alignas(2) test2
//{
//	char a;
//	short b;
//};

#include "union_findset.h"
#include "graph.h"
#include "LRUCache.h"
#include "BTree.h"
#include "skiplist.h"
//高阶数据结构
//
//并查集		union-findset
//在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合
//开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并
//在此过程中要反复用到查询某一个元素归属于那个集合的运算
//适合于描述这类问题的抽象数据类型称为并查集
//底层结构:	数组
//	数组的下标对应集合中元素的编号
//	数组中如果为负数，负号代表根，数字代表该集合中元素个数
//	数组中如果为非负数，代表该元素双亲在数组中的下标
//并查集操作
//查找元素属于哪个集合
//	沿着数组表示树形关系以上一直找到根	(即:树中中元素为负数的位置)
//查看两个元素是否属于同一个集合
//	沿着数组表示的树形关系往上一直找到树的根，如果根相同表明在同一个集合，否则不在
//将两个集合归并成一个集合
//	将两个集合中的元素合并,将一个集合名称改成另一个集合的名称
//集合的个数
//	遍历数组,数组中元素为负数的个数即为集合的个数
//
//图
//图是由顶点集合及顶点间的关系组成的一种数据结构:	G = (V, E)
//	顶点集合  V = {   x    | x属于某个数据对象集 }	有穷非空集合
//	顶点间关系E = { (x,y)  |      x,y属于V       }
//			  或= { <x, y> | x,y属于V && Path(x, y) }	有穷集合,边的集合
//	(x, y)表示x到y的一条双向通路,即(x, y)是无方向的
//	Path(x, y)表示从x到y的一条单向通路,即Path(x, y)是有方向的
//	顶点:	图中结点称为顶点,第i个顶点记作vi
//	边:		两个顶点vi和vj相关联称作顶点vi和顶点vj之间有一条边,图中的第k条边记作ek，ek = (vi，vj)或<vi，vj>
//	有向图:	在有向图中,顶点对<x, y>是有序的,顶点对<x，y>称为顶点x到顶点y的一条边(弧)
//				<x, y>和<y, x>是两条不同的边
//	无向图:	在无向图中,顶点对(x, y)是无序的,顶点对(x, y)称为顶点x和顶点y相关联的一条边,这条边没有特定方向
//				(x, y)和(y，x)是同一条边
//		注意:	无向边(x, y)等于有向边<x, y>和<y, x>
//	完全图:
//			在有n个顶点的无向图中,若有n * (n - 1) / 2条边,即任意两个顶点之间有且仅有一条边,则称此图为无向完全图
//			在n个顶点的有向图中，若有n* (n - 1)条边,即任意两个顶点之间有且仅有方向相反的边,则称此图为有向完全图
//	邻接顶点:
//			在无向图中G中,若(u, v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u, v)依附于顶点u和v
//			在有向图G中,若<u, v>是E(G)中的一条边,则称顶点u邻接到v,顶点v邻接自顶点u,并称边<u, v>与顶点u和顶点v相关联
//	顶点的度:
//			顶点v的度是指与它相关联的边的条数,记作deg(v)
//			在有向图中,顶点的度等于该顶点的入度与出度之和,即dev(v) = indev(v) + outdev(v)
//				顶点v的入度:	以v为终点的有向边的条数,记作indev(v)
//				顶点v的出度:	以v为起始点的有向边的条数,记作outdev(v)
//				
//			在无向图中,顶点的度等于该顶点的入度和出度,即dev(v) = indev(v) = outdev(v)
//	路径:	在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从顶点vi到顶点vj的路径
//	路径长度:
//			对于不带权的图,一条路径的路径长度是指该路径上的边的条数
//			对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和
//				权值:	边附带的数据信息
//	简单路径:
//			若路径上各顶点v1，v2，v3, ... ,vm均不重复,则称这样的路径为简单路径
//	回路:	若路径上第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环
//	子图:	设图G = {V, E}和图G1 = {V1, E1},若V1属于V且E1属于E,则称G1是G的子图
//	连通图:
//			在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的
//			如果图中任意一对顶点都是连通的,则称此图为连通图
//	强连通图:
//			在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到vi的路径,则称此图是强连通图
//	生成树:	一个连通图的最小连通子图称作该图的生成树
//			有n个顶点的连通图的生成树有n个顶点和n - 1条边
//
//图的存储结构
//	图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和边关系即可
//	节点保存只需要一段连续空间即可,边保存需要邻接矩阵/邻接表
//邻接矩阵
//	因为节点与节点之间的关系就是连通与否,即为0或者1
//	因此邻接矩阵(二维数组)即是:	先用一个数组将定点保存
//								然后采用矩阵来表示节点与节点之间的关系
//注意:
//	1.	无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度
//		有向图的邻接矩阵则不一定是对称的,第i行(列)元素之后就是顶点i 的出(入)度
//	2.	如果边带有权值,并且两个节点之间是连通的,边的关系就用权值代替
//						如果两个顶点不通,则使用无穷大代替
//	3.	用邻接矩阵存储图的有点是能够快速知道两个顶点是否连通
//		缺陷是如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间
//				并且要求两个节点之间的路径不是很好求
//邻接表
//使用数组表示顶点的集合,使用链表表示边的关系
//	无向图中同一条边在邻接表中出现了两次
//		顶点vi的度:	顶点vi边链表集合中结点的数目
//	有向图中每条边在邻接表中只出现一次
//		与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表
//			vi顶点的入度,需检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i
//
//最小生成树
//连通图中的每一棵生成树,都是原图的一个极大无环子图
//	即:	从其中删去任何一条边,生成树就不在连通
//		反之,在其中引入任何一条新边,都会形成一条回路
//若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边
//因此构造最小生成树的准则有三条:
//	1.	只能使用图中的边来构造最小生成树
//	2.	只能使用恰好n-1条边来连接图中的n个顶点
//	3.	选用的n-1条边不能构成回路
//构造最小生成树的方法:
//	Kruskal算法和Prim算法	都采用了逐步求解的贪心策略
//贪心算法:
//	是指在问题求解时,总是做出当前看起来最好的选择,贪心算法做出的不是整体最优的选择,而是某种意义上的局部最优解\
//	贪心算法不是对所有的问题都能得到整体最优解
//Kruskal算法
//	任给一个有n个顶点的连通网络N = {V, E}
//	首先构造一个由这n个顶点组成,不含任何边的图G = {V, NULL},其中每个顶点自成一个连通分量
//	其次不断从E中取出权值最小的一条边(若有多条任取其一),若该边的两个顶点来自不同的连通分量,则将此边加入到G中
//	如此重复直到所有顶点在同一个连通分量上为止
//核心:	每次迭代时，选出一条具有最小权值，且两端点不在同一连通分量上的边,加入生成树
//Prim算法
//Prim算法工作原理与Dijkstra的最短路径算法相似
//Prim算法所具有的一个性质是集合A中的边总是构成一棵树
//该树从一个任意根结点r开始,一直长到覆盖V中所有结点为止
//算法每一步在连接集合A和A之外节点的所有边中选出轻量级边加入A
// 当算法终止时,A中的边形成一颗最小生成树
//贪心策略:	每一步所加入的边都是使树总权值增加最小的
//
//最短路径
//从在带权有向图G中的某一顶点出发,找出一条通往另一顶点的最短路径,最短也就是沿路径各边的权值总和达到最小
//
//单源最短路径问题:
//	给定一个图G = (V, E),求源结点s ∈ V到图中每个结点v ∈ V的最短路径
//单源最短路径--Dijkstra算法
//Dijkstra算法就适用于解决带权重的有向图上的单源最短路径问题,算法要求图中所有边的权重非负
//一般在求解最短路径的时候都是已知一个起点和一个终点,所以求解过后也就得到了从起点到终点的最短路径
//针对一个带权有向图G,将所有结点分为两组S和Q
//	S是已经确定最短路径的结点集合,在初始时为空(初始时就可以将源节点s放入),Q为其余未确定最短路径的结点集合
//每次从Q中找出一个起点到该结点代价最小的结点u,将u从Q中移出,并放入S中,对u的每一个相邻结点v进行松弛操作
//	松弛即对每一个相邻结点v,判断源节点s到结点u的代价与u到v的代价之和是否比原来s到v的代价更小,若代价比原来小则要将s到v的代价更新为s到u与u到v的代价之和否则维持原样
//如此一直循环直至集合Q为空,即所有节点都已经查找过一遍并确定了最短路径
//至于一些起点到达不了的结点在算法循环后其代价仍为初始设定的值,不发生变化
//Dijkstra算法每次都是选择V-S中最小的路径节点来进行更新,并加入S中,所以该算法使用的是贪心策略
//Dijkstra算法存在的问题是不支持图中带负权路径,如果带有负权路径,则可能会找不到一些路径的最短路径
//
//单源最短路径--Bellman-Ford算法
//Dijkstra算法只能用来解决正权图的单源最短路径问题,而Bellman-Ford算法可以解决负权图的单源最短路径问题
//它的优点是可以解决有负权边的单源最短路径问题,而且可以用来判断是否有负权回路
//它也有明显的缺点,它的时间复杂度O(N * E)(N是点数,E是边数)普遍是要高于Dijkstra算法O(N^2)的
//若使用邻接矩阵实现,那么遍历所有边的数量的时间复杂度就是O(N ^ 3),Bellman-Ford就是一种暴力求解更新
//	负权回路无最短路径解
//
//多源最短路径--Floyd_Warshall算法
//Floyd_Warshall算法是解决任意两点间的最短路径算法
//该算法考虑的是一条最短路径的中间节点	即:	简单路径P = { v1, v2, ... , vn}上除v1和vn的任意节点
//设k是p的一条中间节点,那么从i到j的最短路径p就被分成i到k和k到j的两条最短路径P1,P2
//	P1是从i到k且中间节点属于{i, i+1, ... , k-1}取得一条最短路径
//	P2是从k到j且中间节点属于{k, k+1, ... , j}取得一条最短路径
//原理:		动态规划
//设D(i,j,k)为从i到j的只以(1...k)集合中的节点为中间节点的最短路径长度
//	1. 若最短路径经过顶点k,则D(i,j,k) = D(i,k,k-1) + D(k,j,k-1)
//	1. 若最短路径不经过顶点k,则D(i,j,k) = D(i,j,k-1)
//因此,D(i,j,k) = min(D(i,j,k-1), D(i,k,k-1) + D(k,j,k-1))
//在实际算法中可以直接在原有空间上迭代,以此可降至二维
//Floyd_Warshell算法本质是三维动态规划,D[i][j][k]表示从i到j只经过k个节点的最短路径
//建立转移方程,空间优化为二维,变为最短路径的迭代算法
namespace qyc
{
	namespace test
	{
		void Matrix_Graph()
		{
			cout << "Matrix_Graph" << endl;
			int a[10] = { 0, 1, 2, 3 };
			matrix::graph<int, int, true, INT_MAX> g1(a, 4);
			g1.add_edge(0, 1, 1);
			g1.add_edge(0, 3, 4);

			g1.add_edge(1, 2, 9);
			g1.add_edge(1, 3, 2);

			g1.add_edge(2, 0, 3);
			g1.add_edge(2, 1, 5);
			g1.add_edge(2, 3, 8);

			g1.add_edge(3, 2, 6);

			g1.print();
			cout << endl;

			g1.add_vertex(4);
			g1.print();
			cout << endl;
			g1.remove_vertex(1);
			g1.print();
			cout << endl;
			g1.remove_edge(0, 3);
			g1.print();
			cout << endl;

			cout << endl;
		}
		void Table_Graph()
		{
			cout << "Table_Graph" << endl;
			int a[10] = { 0, 1, 2, 3 };
			table::graph<int, int, true> g1(a, 4);
			g1.add_edge(0, 1, 1);
			g1.add_edge(0, 3, 4);

			g1.add_edge(1, 2, 9);
			g1.add_edge(1, 3, 2);

			g1.add_edge(2, 0, 3);
			g1.add_edge(2, 1, 5);
			g1.add_edge(2, 3, 8);

			g1.add_edge(3, 2, 6);

			g1.print();
			cout << endl;

			g1.add_vertex(4);
			g1.print();
			cout << endl;
			g1.remove_vertex(1);
			g1.print();
			cout << endl;
			g1.remove_edge(0, 3);
			g1.print();
			cout << endl;

			cout << endl;
		}
		void Graph_BFS_DFS()
		{
			cout << "Graph_BFS_DFS" << endl;
			int a[10] = { 0, 1, 2, 3 };
			//matrix::graph<int, int, true, INT_MAX> g1(a, 4);
			table::graph<int, int, true> g1(a, 4);
			g1.add_edge(0, 1, 1);
			g1.add_edge(0, 3, 4);

			g1.add_edge(1, 2, 9);
			g1.add_edge(1, 3, 2);

			g1.add_edge(2, 0, 3);
			g1.add_edge(2, 1, 5);
			g1.add_edge(2, 3, 8);

			g1.add_edge(3, 2, 6);

			g1.print();
			cout << endl;
			vector<int> ret = g1.bfs(1);
			for (auto& e : ret)
				cout << e << ' ';
			cout << endl;
			ret = g1.dfs(1);
			for (auto& e : ret)
				cout << e << ' ';
			cout << endl;
			ret = g1.dfs_r(1);
			for (auto& e : ret)
				cout << e << ' ';
			cout << endl;
		}
		void Kruskal_MinTree()
		{
			cout << "Graph_BFS_DFS" << endl;

			const char* str = "abcdefghi";
			matrix::graph<char, int, false> g1(str, strlen(str));
			//table::graph<char, int, false> g1(str, strlen(str));
			//matrix::graph<char, int, true> g1(str, strlen(str));
			//table::graph<char, int, true> g1(str, strlen(str));
			
			g1.add_edge('a', 'b', 4);
			g1.add_edge('a', 'h', 8);
			g1.add_edge('b', 'c', 8);
			g1.add_edge('b', 'h', 11);
			g1.add_edge('c', 'i', 2);
			g1.add_edge('c', 'f', 4);
			g1.add_edge('c', 'd', 7);
			g1.add_edge('d', 'f', 14);
			g1.add_edge('d', 'e', 9);
			g1.add_edge('e', 'f', 10);
			g1.add_edge('f', 'g', 2);
			g1.add_edge('g', 'h', 1);
			g1.add_edge('g', 'i', 6);
			g1.add_edge('h', 'i', 7);

			g1.print();
			
			pair<matrix::graph<char, int, false>, int> mintree = g1.Kruskal_mintree();
			//pair<table::graph<char, int, false>, int> mintree = g1.Kruskal_mintree();
			//pair<matrix::graph<char, int, true>, int> mintree = g1.Kruskal_mintree();
			//pair<table::graph<char, int, true>, int> mintree = g1.Kruskal_mintree();
			cout << mintree.second << endl;
			mintree.first.print();
			//vector<char> ret = mintree.first.dfs_r('g');
			//for (char& e : ret)
			//	cout << e << ' ';
			//cout << endl;

			mintree = g1.Prim_mintree();
			cout << mintree.second << endl;
			mintree.first.print();

			cout << endl;
		}
		void Dijkstra_ShortPath()
		{
			const char* str = "syztx";
			matrix::graph<char, int, true> g1(str, strlen(str));
			//table::graph<char, int, true> g1(str, strlen(str));
			g1.add_edge('s', 't', 10);
			g1.add_edge('s', 'y', 5);
			g1.add_edge('y', 't', 3);
			g1.add_edge('y', 'x', 9);
			g1.add_edge('y', 'z', 2);
			g1.add_edge('z', 's', 7);
			g1.add_edge('z', 'x', 6);
			g1.add_edge('t', 'y', 2);
			g1.add_edge('t', 'x', 1);
			g1.add_edge('x', 'z', 4);
			cout << "Dijkstra" << endl;
			g1.print_shortpath(g1.Dijkstra('s'));
			cout << "Bellman_Ford" << endl;
			g1.print_shortpath(g1.Bellman_Ford('s'));
		}
		void Folyd_Warshell()
		{
			const char* str = "syztx";
			//matrix::graph<char, int, true> g1(str, strlen(str));
			table::graph<char, int, true> g1(str, strlen(str));
			g1.add_edge('s', 't', 10);
			g1.add_edge('s', 'y', 5);
			g1.add_edge('y', 't', 3);
			g1.add_edge('y', 'x', 9);
			g1.add_edge('y', 'z', 2);
			g1.add_edge('z', 's', 7);
			g1.add_edge('z', 'x', 6);
			g1.add_edge('t', 'y', 2);
			g1.add_edge('t', 'x', 1);
			g1.add_edge('x', 'z', 4);
			g1.print_all_shortpath();
		}
	}
}
//int main()
//{
//	try
//	{
//		//qyc::test::Matrix_Graph();
//		//qyc::test::Table_Graph();
//		//qyc::test::Graph_BFS_DFS();
//		//qyc::test::Kruskal_MinTree();
//		//qyc::test::Dijkstra_ShortPath();
//		qyc::test::Folyd_Warshell();
//	}
//	catch (const exception& e)
//	{
//		cout << e.what() << endl;
//	}
//	return 0;
//}
//
//LRU Cache
//Least Recently Used(最近最少使用),一种Cache替换算法
//Cache
//狭义的Cache指的是位于CPU和主存间的快速RAM
//	通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术
//广义上的Cache指的是位于速度相差较大的两种硬件之间,用于协调两者数据传输速度差异的结构
//除了CPU与主存之间有Cache,内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache(Internet临时文件夹或网络内容缓存等)
//Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时,就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容
//LRU Cache的替换原则就是将最近最少使用的内容替换掉
//其实,LRU译成最久未使用会更形象,因为该算法每次替换掉的就是一段时间内最久没有使用过的内容
//LRU Cache的实现
//实现LRU Cache是要保持高效实现O(1)的put和get
//使用双向链表和哈希表的搭配是最高效和经典的
//	使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除
//	使用哈希表是因为哈希表的增删查改也是O(1)
//
//B树
//常见的搜索结构
//种类			数据格式	时间复杂度
//顺序查找		无要求		O(N)
//二分查找		有序		O(logN)
//二叉搜索树	无要求		O(N)
//二叉平衡树	无要求		O(logN)
//哈希			无要求		O(1)
//以上结构适合用于数据量相对不是很大,能够一次性存放在内存中,进行数据查找的场景
//如果数据量很大,无法一次放进内存中,只能放在磁盘上
//	那么可以考虑将存放关键字及其映射的数据的地址放到一个内存中的搜索树的节点中,要访问数据时,先取这个地址去磁盘访问数据
//使用平衡二叉树搜索树的缺陷:
//	树的高度是logN,这个查找次数在内存中是很快的
//	但是当数据都在磁盘中时,由于访问磁盘速度很慢,在数据量很大时,logN次的磁盘访问效率很低
//使用哈希表的缺陷:
//	哈希表的效率很高是O(1),但是一些极端场景下某个位置冲突很多,导致访问次数剧增效率也不高。
//加速对数据的访问
//	1. 提高IO的速度
//	2. 降低树的高度 -- 多叉树平衡树
//B树概念
//1970年,R.Bayer和E.mccreight提出了一种适合外查找的树,它是一种平衡的多叉树,称为B树
//	(后有一个B的改进版本B+树,然后有些地方的B树写的的是B-树,注意不要误读成"B减树")
//一棵m阶(m > 2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足一下性质
//	1. 根节点至少有两个孩子
//	2. 每个分支节点都包含k-1个关键字和k个孩子,其中ceil(m/2) <= k <= m	ceil是向上取整函数
//	3. 每个叶子节点都包含k-1个关键字,其中ceil(m/2) <= k <= m
//	4. 所有的叶子节点都在同一层
//	5. 每个节点中的关键字从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域划分
//	6. 每个结点的结构为:(n, A0, K1, A1, K2, A2, ..., Kn, An)
//		Ki(1<=i<=n)为关键字,且Ki < Ki + 1(1<=i<=n-1)
//		Ai(0<=i<=n)为指向子树根结点的指针,且Ai所指子树所有结点中的关键字均小于K(i+1)
//		n为结点中关键字的个数,ceil(m/2)-1 <= n <= m-1
//
//插入过程总结:
//	1. 如果树为空,直接插入新节点中,该节点为树的根节点
//	2. 树非空,找待插入元素在树中的插入位置(注意:	找到的插入节点位置一定在叶子节点中)
//	3. 检测是否找到插入位置(假设树中的key唯一,即该元素已经存在时则不插入)
//	4. 按照插入排序的思想将该元素插入到找到的节点中
//	5. 检测该节点是否满足B-树的性质:	即该节点中的元素个数是否等于M,如果小于则满足
//	6. 如果插入后节点不满足B树的性质,需要对该节点进行分裂:
//			申请新节点
//			找到该节点的中间位置
//			将该节点中间位置右侧的元素以及其孩子搬移到新节点中
//			将中间位置元素以及新节点往该节点的双亲节点中插入,即继续步骤4
//	7. 如果向上已经分裂到根节点的位置,插入结束
//B-树的性能分析
//对于一棵节点为N度为M的B-树,查找和插入需要 log{M-1}N -- log{M/2}N 次比较,
//	对于度为M的B-树,每一个节点的子节点个数为M/2 -- (M-1)之间,因此树的高度应该在要log{M-1}N -- log{M/2}N之间
//	在定位到该节点后,再采用二分查找的方式可以很快的定位到该元素
//B-树的效率是很高的,对于 N= 62*1000000000个节点,如果度M为1024,则 log{M/2}N <= 4
//	即在620亿个元素中,如果这棵树的度为1024,则需要小于4次即可定位到该节点
//	然后利用二分查找可以快速定位到该元素,大大减少了读取磁盘的次数
//B+树
//B+树是B树的变形,是在B树基础上优化的多路平衡搜索树
//	规则基本类似,在B树的基础上做了以下几点改进优化
//	1. 分支节点的子树指针与关键字个数相同
//	2. 分支节点的子树指针p[i]指向关键字值大小在[k[i]，k[i + 1])区间之间
//	3. 所有叶子节点增加一个链接指针链接在一起
//	4. 所有关键字及其映射数据都在叶子节点出现
//		分支节点和叶子节点存有重复值,分支节点存的是叶子节点索引
//		父亲中存的是孩子节点中的最小值做索引	分支节点只存key,叶子节点存key/value
//B+树的特性:
//	1. 所有关键字都出现在叶子节点的链表中,且链表中的节点都是有序的
//	2. 不可能在分支节点中命中
//	3. 分支节点相当于是叶子节点的索引,叶子节点才是存储数据的数据层
//B*树
//B*树是B+树的变形,在B+树的非根和非叶子节点再增加指向兄弟节点的指针
//B+树的分裂:
//当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针
//B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针
//B*树的分裂:
//当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中
//	再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了)
//	如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针
//所以,B*树分配新结点的概率比B+树要低,空间使用率更高
//总结
//将B树,B+树,B*树总结如下:
//B树:		有序数组 + 平衡多叉树
//B+ 树:	有序数组链表 + 平衡多叉树
//B* 树:	一棵更丰满的,空间利用率更高的B+树
//
//B-树的应用
//索引
//B-树最常见的应用就是用来做索引
//MySQL官方对索引的定义为:
//	索引(index)是帮助MySQL高效获取数据的数据结构,(就是数据结构)
//当数据量很大时,为了能够方便管理数据,提高数据查询的效率,一般都会选择将数据保存到数据库
//	因此数据库不仅仅是帮助用户管理数据,而且数据库系统还维护着满足特定查找算法的数据结构
//	这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法	该数据结构就是索引
//MySQL索引简介
//mysql是目前非常流行的开源关系型数据库,免费,可靠性高,速度比较快,拥有灵活的插件式存储引擎
//MySQL中索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的
//	索引是基于表的,而不是基于数据库的
namespace qyc
{
	namespace test
	{
		void BTree_Test()
		{
			vector<int> x = { 53, 139, 75, 49, 145, 36, 101 };
			BTree<int, 3> bt(x.begin(), x.end());
			bt.in_order();
		}
	}
}
//int main()
//{
//	qyc::test::BTree_Test();
//	return 0;
//}
//
//跳表	skiplist
//本质上也是一种查找结构,用于解决算法中的查找问题,和平衡搜索树和哈希表的价值是一样的
//	可以作为key或者key/value的查找模型
//skiplist是由William Pugh发明,最早出现于1990年发表的论文<<Skip Lists : A Probabilistic Alternative to Balanced Trees>>
//skiplist,是一个list,在有序链表的基础上发展起来
//	一个有序的链表,查找数据的时间复杂度是O(N)
//William Pugh开始的优化思路:
//	假如每相邻两个节点升高一层,增加一个指针,让指针指向下下个节点
//		所有新增加的指针连成了一个新的链表,其包含的节点个数只有原来的一半
//		由于新增加的指针,不再需要与链表中每个节点逐个进行比较了,需要比较的节点数只有原来的一半
//	以此类推,我们可以在第二层新产生的链表上,继续为每相邻的两个节点升高一层,增加一个指针,从而产生第三层链表
//	skiplist正是受这种多层链表的想法的启发而设计出来的
//实际上按上面生成链表的方式,上面每一层链表的节点个数,是下面一层的节点个数的一半
//	查找过程就非常类似二分查找,使得查找的时间复杂度可以降低到O(logn)
//但是这个结构在插入或者删除一个节点之后,就会打乱上下相邻两层链表上节点个数严格的2 : 1的对应关系
//若要维持这种对应关系,就必须把新插入的节点后面的所有节点(也包括新插入的节点)重新进行调整,这会让时间复杂度重新蜕化成O(n)
//skiplist的设计为了避免这种问题,不再严格要求对应比例关系,而是插入一个节点的时候随机出一个层数
//	每次插入和删除都不需要考虑其他节点的层数,就好处理多了
//一般跳表会设计一个最大层数maxLevel的限制,其次会设置一个多增加一层的概率p
//随机层数伪代码
//	randomlevel()
//		lvl := 1
//		while random() < p and lvl < MaxLevel do
//			lvl := lvl + 1
//		return lvl
//在Redis的skiplist实现中,这两个参数的取值为 p = 1/4 maxlevel = 32
//根据前面randomLevel()的伪码,可以看出,产生越高的节点层数,概率越低
//	节点层数至少为1,而大于1的节点层数,满足一个概率分布
//	节点层数恰好等于1的概率为1 - p
//	节点层数大于等于2的概率为p,而节点层数恰好等于2的概率为p * (1 - p)
//	节点层数大于等于3的概率为p^2,而节点层数恰好等于3的概率为p^2 * (1 - p)
//	节点层数大于等于4的概率为p^3,而节点层数恰好等于4的概率为p^3 * (1 - p)
//因此,一个节点的平均层数(也即包含的平均指针数目),计算如下:
//	1*(1-p) + 2p(1-p) + 3p^2(1-p) + ... + np^(n-1)(1-p) = 1 / (1 - p)
//跳表的时间复杂度为O(logN)
//skiplist跟平衡搜索树和哈希表的对比
//相比平衡搜索树(AVL树和红黑树)		都可以做到遍历数据有序,时间复杂度也差不多
//	skiplist的优势:	skiplist实现简单,容易控制	平衡树增删查改遍历都更复杂
//					skiplist的额外空间消耗更低	平衡树节点存储每个值有三叉链,平衡因子/颜色等消耗
//相比哈希表而言	优势不大
//	哈希表平均时间复杂度是O(1),比skiplist快	哈希表空间消耗略多一点
//	skiplist优势:	skiplist空间消耗略小一点,遍历数据有序
//					哈希表存在链接指针和表空间消耗,扩容有性能损耗
//					哈希表再极端场景下哈希冲突高,效率下降厉害,需要红黑树补足接力
//namespace qyc
//{
//	namespace test
//	{
//		void SkipList_Test()
//		{
//			skiplist<int, int, qyc::test::KeyOfT<int>, qyc::greater<int>> test;
//			for (int i = 0; i < 10; ++i)
//			{
//				test.insert(i);
//			}
//			for (auto e : test)
//				cout << e << ' ';
//			cout << endl;
//			for (skiplist<int, int, qyc::test::KeyOfT<int>, qyc::greater<int>>::const_reverse_iterator rit = test.rbegin(); rit != test.rend(); ++rit)
//				cout << *rit << ' ';
//			cout << endl;
//			for (int i = 0; i < 10; ++i)
//			{
//				test.erase(i);
//				for (auto e : test)
//					cout << e << ' ';
//				cout << endl;
//			}
//		}
//	}
//}
//int main()
//{
//	qyc::test::SkipList_Test();
//	return 0;
//}

//高并发内存池测试
#include "object_memory_pool.h"
#include "concurrent_memory_pool/concurrent_memory_pool.h"
#include "radix_tree.h"
struct my_struct
{
	my_struct(int a = 0, int b = 0)
		: _a(a)
		, _b(b)
	{}

	~my_struct()
	{
		_a = 0, _b = 0;
	}

	int _a = 0;
	int _b = 0;
};
namespace qyc
{
	namespace test
	{
		void ObjectPool_Test()
		{
			srand((unsigned int)time(nullptr));
			object_pool<my_struct> test;
			for (size_t i = 0; true; ++i)
			{
				vector<my_struct*> v;
				v.resize(rand() % 1000);
				for (size_t i = 0; i < v.size(); ++i)
					v[i] = test.allocate();
				for (size_t i = 0; i < v.size(); ++i)
					test.deallocate(v[i]);
				std::cout << "第" << i << "次运行malloc_test" << std::endl;
			}
		}
		void Destroy_Test(thread_cache** ptr)
		{
			srand((unsigned int)time(nullptr));
			size_t size = rand() % 100 ;
			for (int i = 0; i < 5; ++i)
			{
				void* ptr = concurrent_alloc(size);
				((char*)ptr)[0] = 'H';
				((char*)ptr)[1] = 'e';
				((char*)ptr)[2] = 'l';
				((char*)ptr)[3] = 'l';
				((char*)ptr)[4] = 'o';
				((char*)ptr)[5] = ' ';
				((char*)ptr)[6] = 'W';
				((char*)ptr)[7] = 'o';
				((char*)ptr)[8] = 'r';
				((char*)ptr)[9] = 'l';
				((char*)ptr)[10] = 'd';
				((char*)ptr)[11] = '\0';
				std::cout << (char*)ptr << std::endl;
				concurrent_free(ptr);
			}
			*ptr = pTLS_thread_cache.get();
		}
		thread_cache* ThreadCache_destroy_Test()
		{
			//线程销毁释放thread_cache测试
			thread_cache* ptr = nullptr;
			std::thread t(Destroy_Test, &ptr);
			
			t.join();
			return ptr;
		}
		void Malloc_Test()
		{
			srand((unsigned int)time(nullptr));
			vector<pair<void*, size_t>> ptrs;
			//ptrs.resize(10);
			ptrs.resize(rand() % 1000);
			for (int i = 0; i < ptrs.size(); ++i)
			{
				//size_t size = rand() % (129 << page_info.get_shift());
				//size_t size = rand() % 128 + 1;
				//size_t size = (rand() % (56 * 1024)) + 8 * 1024 + 1;
				//size_t size = (rand() % (192 * 1024)) + 64 * 1024 + 1;
				size_t size = rand() % qyc::thread_cache_max_bytes + 1;
				ptrs[i] = make_pair(concurrent_alloc(size), size);
			}
			for (int i = 0; i < ptrs.size(); ++i)
				concurrent_free(ptrs[i].first);
		}
		void Big_Memory_Test()
		{
			size_t size = rand() % (qyc::thread_cache_max_bytes * 2) + qyc::thread_cache_max_bytes + 1;
			void* ptr = concurrent_alloc(size);
			concurrent_free(ptr);
		}
		void TLS_Test()
		{
			//多线程并发访问测试
			vector<std::thread*> vt;
			vt.resize(100);
			for (size_t i = 0; i < vt.size(); ++i)
			{
				vt[i] = new std::thread
					([]() -> void
					{
						try
						{
							Malloc_Test();
							Big_Memory_Test();
						}
						catch (const exception& e)
						{
							std::cout << e.what() << std::endl;
						}
						catch (...)
						{
							std::cout << "unkonw  exception" << std::endl;
						}
					});
			}
			for (size_t i = 0; i < vt.size(); ++i)
				vt[i]->join();
		}
		void Radix_Test()
		{
			const size_t bits = 52;
			radix_tree_three_level<bits> t1;
			for (size_t i = 1 << 16; i < ((size_t)1 << 18); i++)
				t1[i] = (void*)i;
			for (size_t i = 1 << 16; i < ((size_t)1 << 18); i++)
				std::cout << t1[i] << std::endl;
			std::cout << "OK" << std::endl;
		}
	}
}
int main()
{
	try
	{
		//qyc::test::Radix_Test();
		for (size_t i = 0; i < 50; ++i)
		{
			//qyc::test::ObjectPool_Test();
			//qyc::thread_cache* ptr = qyc::test::ThreadCache_destroy_Test();
			//qyc::test::Malloc_Test();
			//qyc::test::Big_Memory_Test();
			qyc::test::TLS_Test();

			std::cout << "第" << i << "次运行malloc_test" << std::endl;
		}
	}
	catch (const exception& e)
	{
		std::cout << e.what() << std::endl;
	}
	catch (...)
	{
		std::cout << "unkonw  exception" << std::endl;
	}
	return 0;
}